diff --git a/.github/workflows/ci-interpreter.yml b/.github/workflows/ci-interpreter.yml index 91a5fc423..bb507f1ce 100644 --- a/.github/workflows/ci-interpreter.yml +++ b/.github/workflows/ci-interpreter.yml @@ -2,11 +2,11 @@ name: CI for interpreter & tests on: push: - branches: [ main ] + branches: [ main, wasm-3.0 ] paths: [ .github/**, interpreter/**, test/** ] pull_request: - branches: [ main ] + branches: [ main, wasm-3.0 ] paths: [ .github/**, interpreter/**, test/** ] # Allows you to run this workflow manually from the Actions tab @@ -17,20 +17,20 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup OCaml - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: 4.14.x - name: Setup OCaml tools run: opam install --yes ocamlfind.1.9.5 js_of_ocaml.4.0.0 js_of_ocaml-ppx.4.0.0 - name: Setup Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: - node-version: 19.x + node-version: 20.x - name: Build interpreter run: cd interpreter && opam exec make - name: Run tests # TODO: reactiate node once it supports all of Wasm 3.0 # run: cd interpreter && opam exec make JS=node ci - run: cd interpreter && opam exec make ci + run: cd interpreter && opam exec make test diff --git a/.github/workflows/ci-spec.yml b/.github/workflows/ci-spec.yml index a0aae6a04..82447e640 100644 --- a/.github/workflows/ci-spec.yml +++ b/.github/workflows/ci-spec.yml @@ -2,11 +2,11 @@ name: CI for specs on: push: - branches: [ main ] + branches: [ main, wasm-3.0 ] paths: [ .github/**, document/** ] pull_request: - branches: [ main ] + branches: [ main, wasm-3.0 ] paths: [ .github/**, document/** ] # Allows you to run this workflow manually from the Actions tab @@ -17,11 +17,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: "recursive" - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 16 - name: Setup Bikeshed @@ -35,13 +35,44 @@ jobs: - name: Run Bikeshed run: cd document/core && make bikeshed - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: core-rendered path: document/core/_build/html - build-legacy-exceptions-spec: + build-js-api-spec: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Setup Bikeshed + run: pip install bikeshed && bikeshed update + - name: Run Bikeshed + run: bikeshed spec "document/js-api/index.bs" "document/js-api/index.html" + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: js-api-rendered + path: document/js-api/index.html + + build-web-api-spec: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Setup Bikeshed + run: pip install bikeshed && bikeshed update + - name: Run Bikeshed + run: bikeshed spec "document/web-api/index.bs" "document/web-api/index.html" + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: web-api-rendered + path: document/web-api/index.html + + build-code-metadata-spec: runs-on: ubuntu-latest + needs: [build-core-spec] steps: - name: Checkout repo uses: actions/checkout@v2 @@ -52,29 +83,33 @@ jobs: - name: Setup Sphinx run: pip install six && pip install sphinx==5.1.0 - name: Build main spec - run: cd document/legacy/exceptions && make main + run: cd document/metadata/code && make main - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: legacy-exceptions-rendered - path: document/legacy/exceptions/_build/html + name: code-metadata-rendered + path: document/metadata/code/_build/html - build-js-api-spec: + build-legacy-exceptions-core-spec: runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@v2 - - name: Setup Bikeshed - run: pip install bikeshed && bikeshed update - - name: Run Bikeshed - run: bikeshed spec "document/js-api/index.bs" "document/js-api/index.html" + with: + submodules: "recursive" + - name: Setup TexLive + run: sudo apt-get update -y && sudo apt-get install -y latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended + - name: Setup Sphinx + run: pip install six && pip install sphinx==5.1.0 + - name: Build main spec + run: cd document/legacy/exceptions/core && make main - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: js-api-rendered - path: document/js-api/index.html + name: legacy-exceptions-core-rendered + path: document/legacy/exceptions/core/_build/html - build-web-api-spec: + build-legacy-exceptions-js-api-spec: runs-on: ubuntu-latest steps: - name: Checkout repo @@ -82,44 +117,77 @@ jobs: - name: Setup Bikeshed run: pip install bikeshed && bikeshed update - name: Run Bikeshed - run: bikeshed spec "document/web-api/index.bs" "document/web-api/index.html" + run: bikeshed spec "document/legacy/exceptions/js-api/index.bs" "document/legacy/exceptions/js-api/index.html" - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: web-api-rendered - path: document/web-api/index.html + name: legacy-exceptions-js-api-rendered + path: document/legacy/exceptions/js-api/index.html + + build-spec-versions: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: versions-rendered + path: document/versions/ publish-spec: runs-on: ubuntu-latest - needs: [build-core-spec, build-legacy-exceptions-spec, build-js-api-spec, build-web-api-spec] + needs: + - build-core-spec + - build-js-api-spec + - build-web-api-spec + - build-code-metadata-spec + - build-legacy-exceptions-core-spec + - build-legacy-exceptions-js-api-spec + - build-spec-versions steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Create output directory run: mkdir _output && cp document/index.html _output/index.html - name: Download core spec artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: core-rendered path: _output/core - - name: Download legacy exceptions spec artifact - uses: actions/download-artifact@v2 - with: - name: legacy-exceptions-rendered - path: _output/legacy/exceptions - name: Download JS API spec artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: js-api-rendered path: _output/js-api - name: Download Web API spec artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: web-api-rendered path: _output/web-api + - name: Download code metadata spec artifact + uses: actions/download-artifact@v4 + with: + name: code-metadata-rendered + path: _output/metadata/code + - name: Download legacy exceptions core spec artifact + uses: actions/download-artifact@v4 + with: + name: legacy-exceptions-core-rendered + path: _output/legacy/exceptions/core + - name: Download legacy exceptions JS API spec artifact + uses: actions/download-artifact@v4 + with: + name: legacy-exceptions-js-api-rendered + path: _output/legacy/exceptions/js-api + - name: Download spec versions artifacts + uses: actions/download-artifact@v4 + with: + name: versions-rendered + path: _output/versions - name: Publish to GitHub Pages if: github.ref == 'refs/heads/main' - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: publish_dir: ./_output github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/mirror-to-master.yml b/.github/workflows/mirror-to-master.yml deleted file mode 100644 index 7849af75b..000000000 --- a/.github/workflows/mirror-to-master.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Mirror main branch to master branch - -on: - push: - branches: - - 'main' - -jobs: - mirror_job: - runs-on: ubuntu-latest - name: Mirror main branch to master branch - steps: - - name: Mirror branch - uses: google/mirror-branch-action@v1.0 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - source: 'main' - dest: 'master' diff --git a/.github/workflows/w3c-publish.yml b/.github/workflows/w3c-publish.yml index d68183abf..bcb26699d 100644 --- a/.github/workflows/w3c-publish.yml +++ b/.github/workflows/w3c-publish.yml @@ -1,37 +1,69 @@ -name: Publish to W3C TR space +name: Validate/Publish to W3C TR space on: push: branches: [ main ] paths: [ .github/**, document/** ] + pull_request: + paths: [ .github/**, document/** ] - # Allows you to run this workflow manually from the Actions tab + # Allows you to run this workflow manually from the Actions tab, gh CLI tool, + # or REST API. THe w3c-status options correspond to the valid options for + # Bikeshed's --md-status flag, and refer to the W3C rec-track document + # stages described in https://www.w3.org/policies/process/#maturity-stages + # (Editor's Draft, Working Draft, Candidiate Recommendation Draft, and + # Candidate Recommendation Snapshot). workflow_dispatch: + inputs: + w3c-status: + required: true + type: choice + description: W3C Document Status + options: + - ED + - WD + - CRD + - CR + +env: + YARN_ENABLE_IMMUTABLE_INSTALLS: false + W3C_STATUS: ${{ github.event_name == 'workflow_dispatch' && inputs.w3c-status || 'WD' }} jobs: publish-to-w3c-TR: - if: github.repository == 'WebAssembly/spec' + strategy: + fail-fast: false + matrix: + spec: [core, js-api, web-api] runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: "recursive" - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 16 - name: Setup Bikeshed run: pip install bikeshed && bikeshed update - name: Setup TexLive + if: ${{ matrix.spec == 'core' }} run: sudo apt-get update -y && sudo apt-get install -y latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended - name: Setup Sphinx + if: ${{ matrix.spec == 'core' }} run: pip install six && pip install sphinx==5.1.0 - - name: Publish all specs to their https://www.w3.org/TR/ URLs - run: cd document && make -e WD-echidna-CI + - name: Publish ${{ matrix.spec }} spec to its https://www.w3.org/TR/ URL + if: env.W3C_ECHIDNA_TOKEN_CORE + run: cd document && make -e -C ${{ matrix.spec }} WD-echidna-CI env: - STATUS: --md-status=WD W3C_ECHIDNA_TOKEN_CORE: ${{ secrets.W3C_ECHIDNA_TOKEN_CORE }} W3C_ECHIDNA_TOKEN_JSAPI: ${{ secrets.W3C_ECHIDNA_TOKEN_JSAPI }} W3C_ECHIDNA_TOKEN_WEBAPI: ${{ secrets.W3C_ECHIDNA_TOKEN_WEBAPI }} - YARN_ENABLE_IMMUTABLE_INSTALLS: false + ECHIDNA_DRYRUN: ${{ !(github.event_name == 'push' && github.repository == 'WebAssembly/spec' && github.ref == 'refs/heads/main') }} + - name: Validate ${{ matrix.spec }} spec with Echidna + if: env.W3C_USERNAME + run: cd document && make -e -C ${{ matrix.spec }} WD-echidna + env: + W3C_USERNAME: ${{ secrets.W3C_USERNAME }} + W3C_PASSWORD: ${{ secrets.W3C_PASSWORD }} diff --git a/README.md b/README.md index 1b4b34b92..4266634cf 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +[![CI for specs](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-spec.yml/badge.svg)](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-spec.yml) +[![CI for interpreter & tests](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-interpreter.yml/badge.svg)](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-interpreter.yml) + # Stack-Switching Proposal for WebAssembly This repository is a clone of [`WebAssembly/spec`](https://github.com/WebAssembly/spec/). It is meant for discussion, prototype specification, and implementation of a proposal to add @@ -5,45 +8,13 @@ support for stack-switching. See the [explainer](proposals/stack-switching/Explainer.md) for a high-level summary of the proposal. -## Previous proposals - -The current explainer represents the unification of two previous proposals: Typed Continuations (wasmfx) and Bag of Stacks (bos). (The explainers have now been unified. Once the reference interpreter and examples are adapted for the unified proposal this section will be removed from the README.) - -#### Typed Continuations - -* See the [explainer](proposals/continuations/Explainer.md) for a high-level summary of the proposal. - -* See the [overview](proposals/continuations/Overview.md) for a more formal description of the proposal. - -* An [implementation](https://github.com/WebAssembly/stack-switching/tree/wasmfx) is available as an extension to the reference interpreter. It is accesible from the `wasmfx` branch of this repository. - -* See the [examples](proposals/continuations/examples) for Wasm code for implementing various different features including lightweight threads, actors, and async/await. - -#### Bag of Stacks Proposal - -* See the [explainer](proposals/bag-o-stacks/Explainer.md) for a high-level summary of the proposal. - -Original README from upstream repository follows. - --------------------------------------------------------------------------------- - -[![CI for specs](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-spec.yml/badge.svg)](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-spec.yml) -[![CI for interpreter & tests](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-interpreter.yml/badge.svg)](https://github.com/WebAssembly/stack-switching/actions/workflows/ci-interpreter.yml) - # spec -This repository holds a prototypical reference implementation for WebAssembly, -which is currently serving as the official specification. Eventually, we expect -to produce a specification either written in human-readable prose or in a formal -specification language. - -It also holds the WebAssembly testsuite, which tests numerous aspects of -conformance to the spec. - -View the work-in-progress spec at [webassembly.github.io/spec](https://webassembly.github.io/spec/). +This repository holds the sources for the WebAssembly specification, +a reference implementation, and the official test suite. -At this time, the contents of this repository are under development and known -to be "incomplet and inkorrect". +A formatted version of the spec is available here: +[webassembly.github.io/spec](https://webassembly.github.io/spec/), Participation is welcome. Discussions about new features, significant semantic changes, or any specification change likely to generate substantial discussion diff --git a/deploy_key b/deploy_key deleted file mode 100644 index f4e5b0005..000000000 --- a/deploy_key +++ /dev/null @@ -1,49 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn -NhAAAAAwEAAQAAAgEAn9FUrQzWcgEOHsDJ+f7L0g7xIYrdBb3K1dQqFWFidZcL8KYMMR0/ -CEYRXfSn3EcvpYnRoIjWfGQgqOpA90FMaAwqdoK72ByW7q2C4ymjX5xR8XFyG5YJVvjaMe -STXZlynQSOH9lXU5RooeEwRatgQizuwB/Bqahry/8FOfDhVlNO26IbEyJPd23qeyAd+42C -OTE5oWHEJ3TKSWSfwTAhfrIlIg+dWiSx/IUcLc+7Ms5EpQh3ebX49oaW00SWRDnyqoM95m -D+Uc3DuTFJyKdHlLouESdNrE9LeBms5N/bHSgedVyZ5fL9SHC6P1HmgDPFspmO6z1s15/a -5y5hf9zHVoj1ha54LSaPlE5/L5hpY6PCH7fepRXhw84VIQV74IsJow1XUbkmfKkjW3PeBq -CF5cTKF2LK65SraxEfNMLU0ThOH6B6yzePq7JF+05VGMh2G2Wkwy11pezeqW5tPU1M2qwS -RN8jAFKIwuC7B73drz8yMhF8MfTW4iGM4RqhQRCK22xmVtzwYFwRKUM++p2iOggGi42jnV -skv7/yQ6XcaEm+2Jx3C2zy/5OdLql9z8gmKsH4jpQUADmae8KBfJCqHZrtvhRTqH99E3th -pIKcpzY+n5uhNCyrY+hTfB44/EIntWkXDTLwVVRmmOyHSvEA7/Dz1vtA7gY9Nu25xY/SXS -sAAAc4dIF4rHSBeKwAAAAHc3NoLXJzYQAAAgEAn9FUrQzWcgEOHsDJ+f7L0g7xIYrdBb3K -1dQqFWFidZcL8KYMMR0/CEYRXfSn3EcvpYnRoIjWfGQgqOpA90FMaAwqdoK72ByW7q2C4y -mjX5xR8XFyG5YJVvjaMeSTXZlynQSOH9lXU5RooeEwRatgQizuwB/Bqahry/8FOfDhVlNO -26IbEyJPd23qeyAd+42COTE5oWHEJ3TKSWSfwTAhfrIlIg+dWiSx/IUcLc+7Ms5EpQh3eb -X49oaW00SWRDnyqoM95mD+Uc3DuTFJyKdHlLouESdNrE9LeBms5N/bHSgedVyZ5fL9SHC6 -P1HmgDPFspmO6z1s15/a5y5hf9zHVoj1ha54LSaPlE5/L5hpY6PCH7fepRXhw84VIQV74I -sJow1XUbkmfKkjW3PeBqCF5cTKF2LK65SraxEfNMLU0ThOH6B6yzePq7JF+05VGMh2G2Wk -wy11pezeqW5tPU1M2qwSRN8jAFKIwuC7B73drz8yMhF8MfTW4iGM4RqhQRCK22xmVtzwYF -wRKUM++p2iOggGi42jnVskv7/yQ6XcaEm+2Jx3C2zy/5OdLql9z8gmKsH4jpQUADmae8KB -fJCqHZrtvhRTqH99E3thpIKcpzY+n5uhNCyrY+hTfB44/EIntWkXDTLwVVRmmOyHSvEA7/ -Dz1vtA7gY9Nu25xY/SXSsAAAADAQABAAACAFMayDxgY5bOw6fsOlscSqKFkJAPpJUat0Hv -3J5XkJpzHAtcXRShD6jevqMr2Knr/nPHMdGXtmjirDUJ8xRfyTqFsQMFQmbDnxyn71ruyP -yrzdSOWHbN0zd9mgC9yn+ujnHl733SR923W51p+u8PibN/p/sRyGPPp5Zhmzcg8hwwn94H -8qpFeisxZe/2qICpeiEBXuVzcEvQKGx3vbb4r0IxoquOkRVR5ZfZI+kSj1aA+iMTPwV0Qe -z32bAshzMdKvnN2z9UCotBQ1imr6Z+jfNhyRi0ZmiGp0jhmQ0+9rK3rPb8Wy6+50RnEgJh -NUpPIauYvD/JJjMN9genD54skR61JnwRSmMUcuYFvcPKip1FYugYtZY/a9waqcSA73TcuZ -DQzihYs4fdr2aD3pH8QchYwo5vZFzPCVuXF387pYUmj8u3RLDhemSYjwuG/NWdVKnYnZ2B -EVOMi4YZ6Kms7rac8zzgFUonxDWLCigOPI0HPfrDKQ7P6NyiAKEEEfK6g2KvnDJaaCdfpb -70UTFG6YyN+1qa0NWVcU6iEGd/ziT7xPDT3WgJd4SAYkllycQkg5zdFz4T1SqABMrWqjK7 -1Xk//kZxboYZFwBoODiNd2dcLU1XOBhNvoAEajKQNyzAhET6eC02olwUwl7ZwdMxMH8C9H -/j4lAq+v6PYzFHN/uZAAABAQCExWknkwPAu+eDulQTW1T5kIhsD3Ffe+9yg1Ps83NPST9w -7dddykxOzZJ4j8WjpsvwCXuxyxPqt3UI3y2t4nC2aIWq5gPoIfDvtt5J8j6i4RwrI2AU3j -tKdPyLD4qKOCvuqThRCUz3GffU8QNknLYT1jGhO8p//nZq68SVIhtcL8CG1/mQQVApgvd+ -VrBIytptBk0wn4ufMY11CjRTLjASJzBsiT/VmUkQVBQDn6/yvCSxx7nYzMt0XcDqH1/KO7 -iqEN6HfkTNTKBXcRWIS+c7OvAixJTGXRCE2xcQindaHQ3HNK+6r1qAXp65XfsTJUw+FIdc -4OXiRdnAAanVy6tAAAABAQDKduhs+s+AKQWsuwkzWdnLQdMP+98vKBkMH7/dBA73xDd+hR -8sHOhoyxYoNoUhvYWcoip2Mqsn/+F5QLvKxaaccQOtfQpYEeB0koL3QEHUIBMwzgEW112T -ATa8KR6okRAUxn7tqswebAFPmdpGS1YkQAyAQQoPr2NQpPSWN0cKXQZUYLn5r6HSZ7isAm -K/6mrF+TqK80055A+duZggLIKyMAKDTdgtIu4D/wZIqZYcY8uiA2ZhGM3XEQutTjo4xemu -V2X+WSwXhrXiSAWqbCBxCRcCLKktweihb1nOkXIOspKr7Adj/ctmlqO875GHuwlrGaNfe2 -DFo67i4udsdrc9AAABAQDKE5rUxfN6K51jWdgiBVx1XVH0YenLGHECYcxg5AHkDTJUOl9F -SqiP4w128fFjHEO+GGltgkruQ94k+wINub/e7k1NYSpgj03BDs6swpRG7Y3uot1aBFAati -ITJF6p1rmjmBxtDhaVX9nA6GyOmzXO4Tb6niBHO5u7B3dqZI/iXHUmsZOsa1ijuE8YL5P7 -SzxbkiGGsWv5gfs8RcYuOmGhTx2LxOTNh3kovj4xQSoJVH3IikpodQAuXTdL5utuAzgVEL -Xpk1XVyVPkFGitmNqTr3D2gKnPnkQf8KYsRmzt4bPKDrKOBleqYbFSabyHUNJEaW7pmf8+ -fNbVF9dWMmyHAAAAAAEC ------END OPENSSH PRIVATE KEY----- diff --git a/deploy_key.pub b/deploy_key.pub deleted file mode 100644 index 313f1bd8e..000000000 --- a/deploy_key.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCf0VStDNZyAQ4ewMn5/svSDvEhit0FvcrV1CoVYWJ1lwvwpgwxHT8IRhFd9KfcRy+lidGgiNZ8ZCCo6kD3QUxoDCp2grvYHJburYLjKaNfnFHxcXIblglW+Nox5JNdmXKdBI4f2VdTlGih4TBFq2BCLO7AH8GpqGvL/wU58OFWU07bohsTIk93bep7IB37jYI5MTmhYcQndMpJZJ/BMCF+siUiD51aJLH8hRwtz7syzkSlCHd5tfj2hpbTRJZEOfKqgz3mYP5RzcO5MUnIp0eUui4RJ02sT0t4Gazk39sdKB51XJnl8v1IcLo/UeaAM8WymY7rPWzXn9rnLmF/3MdWiPWFrngtJo+UTn8vmGljo8Ift96lFeHDzhUhBXvgiwmjDVdRuSZ8qSNbc94GoIXlxMoXYsrrlKtrER80wtTROE4foHrLN4+rskX7TlUYyHYbZaTDLXWl7N6pbm09TUzarBJE3yMAUojC4LsHvd2vPzIyEXwx9NbiIYzhGqFBEIrbbGZW3PBgXBEpQz76naI6CAaLjaOdWyS/v/JDpdxoSb7YnHcLbPL/k50uqX3PyCYqwfiOlBQAOZp7woF8kKodmu2+FFOof30Te2GkgpynNj6fm6E0LKtj6FN8Hjj8Qie1aRcNMvBVVGaY7IdK8QDv8PPW+0DuBj027bnFj9JdKw== diff --git a/document/LICENSE b/document/LICENSE new file mode 100644 index 000000000..726ef38af --- /dev/null +++ b/document/LICENSE @@ -0,0 +1,2 @@ +This document is licensed under the W3C Software and Document License. +Its text can be found at https://www.w3.org/copyright/software-license/ diff --git a/document/Makefile b/document/Makefile index e19d7e7de..77cc20a34 100644 --- a/document/Makefile +++ b/document/Makefile @@ -1,4 +1,4 @@ -DIRS = core js-api web-api legacy/exceptions +DIRS = core js-api web-api metadata/code legacy/exceptions versions FILES = index.html BUILDDIR = _build TAR = tar @@ -28,21 +28,21 @@ diff: $(DIRS:%=diff-%) # macOS: do “brew install tar” & run “make” as: TAR=gtar make -e WD-tar .PHONY: WD-tar WD-tar: - for dir in $(DIRS); \ - do STATUS=--md-status=WD TAR=$(TAR) $(MAKE) -e -C $$dir $@;\ + for dir in $(DIRS); do \ + TAR=$(TAR) $(MAKE) -e -C $$dir $@ || exit 1; \ done # macOS: do “brew install tar” & run “make” as: TAR=gtar make -e WD-echidna .PHONY: WD-echidna WD-echidna: - for dir in $(DIRS); \ - do $(MAKE) -e -C $$dir $@;\ + for dir in $(DIRS); do \ + $(MAKE) -e -C $$dir $@ || exit 1; \ done .PHONY: WD-echidna-CI WD-echidna-CI: - for dir in $(DIRS); \ - do $(MAKE) -e -C $$dir $@;\ + for dir in $(DIRS); do \ + $(MAKE) -e -C $$dir $@ || exit 1; \ done # Directory-specific targets. @@ -60,7 +60,7 @@ $(DIRS:%=build-%): build-%: (cd $(@:build-%=%); make BUILDDIR=$(BUILDDIR) all) .PHONY: $(DIRS:%=dir-%) -$(DIRS:%=dir-%): dir-%: +$(DIRS:%=dir-%): dir-%: $(BUILDDIR) mkdir -p $(BUILDDIR)/$(@:dir-%=%) rm -rf $(BUILDDIR)/$(@:dir-%=%)/* cp -R $(@:dir-%=%)/$(BUILDDIR)/html/* $(BUILDDIR)/$(@:dir-%=%)/ diff --git a/document/core/Makefile b/document/core/Makefile index 74c9daeea..f0016afd8 100644 --- a/document/core/Makefile +++ b/document/core/Makefile @@ -2,6 +2,7 @@ # # You can set these variables from the command line. +W3C_STATUS = ED SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = a4 @@ -9,8 +10,10 @@ BUILDDIR = _build STATICDIR = _static DOWNLOADDIR = _download NAME = WebAssembly -DECISION_URL = https://github.com/WebAssembly/meetings/blob/main/main/2017/WG-12-06.md +DECISION_URL = https://github.com/WebAssembly/meetings/blob/main/main/2024/WG-06-12.md TAR = tar +DEADLINE = $(shell date -d "+30 days" +%Y-%m-%d 2>/dev/null || date -v +30d +%Y-%m-%d) +ECHIDNA_DRYRUN = true # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 @@ -80,7 +83,7 @@ publish-main: clean main bikeshed-keep deploy all: pdf html bikeshed .PHONY: main -main: pdf html +main: macrosok pdf html # Dirty hack to avoid rebuilding the Bikeshed version for every push. .PHONY: bikeshed-keep @@ -97,6 +100,10 @@ GENERATED = appendix/index-instructions.rst %.rst: %.py (cd `dirname $@`; ./`basename $^`) +.PHONY: macrosok +macrosok: $(GENERATED) + sh util/check_macros.sh + .PHONY: pdf pdf: $(GENERATED) latexpdf mkdir -p $(BUILDDIR)/html/$(DOWNLOADDIR) @@ -150,7 +157,7 @@ bikeshed: $(GENERATED) @echo @echo ========================================================================= mkdir -p $(BUILDDIR)/bikeshed_mathjax/ - bikeshed spec $(STATUS) index.bs $(BUILDDIR)/bikeshed_mathjax/index.html + bikeshed spec --md-status=$(W3C_STATUS) --md-deadline=$(DEADLINE) index.bs $(BUILDDIR)/bikeshed_mathjax/index.html mkdir -p $(BUILDDIR)/html/bikeshed/ (cd util/katex/ && yarn && yarn build && npm install --only=prod) python3 util/mathjax2katex.py $(BUILDDIR)/bikeshed_mathjax/index.html \ @@ -190,9 +197,11 @@ WD-echidna: WD-tar curl 'https://labs.w3.org/echidna/api/request' \ --user '$(W3C_USERNAME):$(W3C_PASSWORD)' \ -F "tar=@$(BUILDDIR)/WD.tar" \ - -F "decision=$(DECISION_URL)" | tee $(BUILDDIR)/WD-echidna-id.txt + -F "decision=$(DECISION_URL)" \ + -F "dry-run=$(ECHIDNA_DRYRUN)" | tee $(BUILDDIR)/WD-echidna-id.txt + python3 ../util/check-echidna-status.py $(BUILDDIR) @echo - @echo "Published working draft. Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" + @echo "Uploaded $(W3C_STATUS). Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" .PHONY: WD-echidna-CI WD-echidna-CI: WD-tar @@ -204,9 +213,11 @@ WD-echidna-CI: WD-tar curl 'https://labs.w3.org/echidna/api/request' \ -F "tar=@$(BUILDDIR)/WD.tar" \ -F "token=$(W3C_ECHIDNA_TOKEN_CORE)" \ - -F "decision=$(DECISION_URL)" | tee $(BUILDDIR)/WD-echidna-id.txt + -F "decision=$(DECISION_URL)" \ + -F "dry-run=$(ECHIDNA_DRYRUN)" | tee $(BUILDDIR)/WD-echidna-id.txt + python3 ../util/check-echidna-status.py $(BUILDDIR) @echo - @echo "Published working draft. Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" + @echo "Uploaded $(W3C_STATUS). Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" .PHONY: diff diff: bikeshed diff --git a/document/core/appendix/changes.rst b/document/core/appendix/changes.rst index 3db3891a1..1221518f7 100644 --- a/document/core/appendix/changes.rst +++ b/document/core/appendix/changes.rst @@ -12,27 +12,31 @@ Release 2.0 .. index:: instruction, integer -Sign extension instructions +Sign Extension Instructions ........................... Added new numeric instructions for performing sign extension within integer representations. [#proposal-signext]_ -* New :ref:`numeric instructions `: :math:`\K{i}\X{nn}\K{.}\EXTEND\X{N}\K{\_s}` +* New :ref:`numeric instructions `: + + - :math:`\K{i}\X{nn}\K{.}\EXTEND\X{N}\K{\_s}` .. index:: instruction, trap, floating-point, integer -Non-trapping float-to-int conversions +Non-trapping Float-to-Int Conversions ..................................... Added new conversion instructions that avoid trapping when converting a floating-point number to an integer. [#proposal-cvtsat]_ -* New :ref:`numeric instructions `: :math:`\K{i}\X{nn}\K{.}\TRUNC\K{\_sat\_f}\X{mm}\K{\_}\sx` +* New :ref:`numeric instructions `: + + - :math:`\K{i}\X{nn}\K{.}\TRUNC\K{\_sat\_f}\X{mm}\K{\_}\sx` .. index:: block, function, value type, result type -Multiple values +Multiple Values ............... Generalized the result type of blocks and functions to allow for multiple values; in addition, introduced the ability to have block parameters. [#proposal-multivalue]_ @@ -44,56 +48,90 @@ Generalized the result type of blocks and functions to allow for multiple values .. index:: value type, reference, reference type, instruction, element segment -Reference types +Reference Types ............... Added |FUNCREF| and |EXTERNREF| as new value types and respective instructions. [#proposal-reftype]_ -* New :ref:`value types `: :ref:`reference types ` |FUNCREF| and |EXTERNREF| +* New :ref:`reference ` :ref:`value types `: + + - |FUNCREF| + - |EXTERNREF| + +* New :ref:`reference instructions `: -* New :ref:`reference instructions `: |REFNULL|, |REFFUNC|, |REFISNULL| + - |REFNULL| + - |REFFUNC| + - |REFISNULL| -* Extended :ref:`parametric instruction `: |SELECT| with optional type immediate +* Extended :ref:`parametric instruction `: + + - |SELECT| with optional type immediate * New :ref:`declarative ` form of :ref:`element segment ` .. index:: reference, instruction, table, table type -Table instructions +Table Instructions .................. Added instructions to directly access and modify tables. [#proposal-reftype]_ * :ref:`Table types ` allow any :ref:`reference type ` as element type -* New :ref:`table instructions `: |TABLEGET|, |TABLESET|, |TABLESIZE|, |TABLEGROW| +* New :ref:`table instructions `: + + - |TABLEGET| + - |TABLESET| + - |TABLESIZE| + - |TABLEGROW| .. index:: table, instruction, table index, element segment, import, export -Multiple tables +Multiple Tables ............... Added the ability to use multiple tables per module. [#proposal-reftype]_ -* :ref:`Modules ` may :ref:`define `, :ref:`import `, and :ref:`export ` multiple tables +* :ref:`Modules ` may + + - :ref:`define ` multiple tables + - :ref:`import ` multiple tables + - :ref:`export ` multiple tables -* :ref:`Table instructions ` take a :ref:`table index ` immediate: |TABLEGET|, |TABLESET|, |TABLESIZE|, |TABLEGROW|, |CALLINDIRECT| +* :ref:`Table instructions ` take a :ref:`table index ` immediate: + + - |TABLEGET| + - |TABLESET| + - |TABLESIZE| + - |TABLEGROW| + - |CALLINDIRECT| * :ref:`Element segments ` take a :ref:`table index ` .. index:: instruction, table, memory, data segment, element segment -Bulk memory and table instructions +Bulk Memory and Table Instructions .................................. Added instructions that modify ranges of memory or table entries. [#proposal-reftype]_ [#proposal-bulk]_ -* New :ref:`memory instructions `: |MEMORYFILL|, |MEMORYINIT|, |MEMORYCOPY|, |DATADROP| +* New :ref:`memory instructions `: + + - |MEMORYFILL| + - |MEMORYINIT| + - |MEMORYCOPY| + - |DATADROP| -* New :ref:`table instructions `: |TABLEFILL|, |TABLEINIT|, |TABLECOPY|, |ELEMDROP| +* New :ref:`table instructions `: + + - |TABLEFILL| + - |TABLEINIT| + - |TABLECOPY| + - |ELEMDROP| * New :ref:`passive ` form of :ref:`data segment ` @@ -106,36 +144,131 @@ Added instructions that modify ranges of memory or table entries. [#proposal-ref .. index:: instructions, SIMD, value type, vector type -Vector instructions +Vector Instructions ................... -Added vector type and instructions that manipulate multiple numeric values in parallel (also known as *SIMD*, single instruction multiple data) [#proposal-vectype]_ - -* New :ref:`value type `: |V128| - -* New :ref:`memory instructions `: :math:`\K{v128.}\LOAD`, :math:`\K{v128.}\LOAD{}\!N\!\K{x}\!M\!\K{\_}\sx`, :math:`\K{v128.}\LOAD{}N\K{\_zero}`, :math:`\K{v128.}\LOAD{}N\K{\_splat}`, :math:`\K{v128.}\LOAD{}N\K{\_lane}`, :math:`\K{v128.}\STORE`, :math:`\K{v128.}\STORE{}N\K{\_lane}` - -* New constant :ref:`vector instruction `: :math:`\K{v128.}\VCONST` - -* New unary :ref:`vector instructions `: :math:`\K{v128.not}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.abs}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.neg}`, :math:`\K{i8x16.popcnt}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.abs}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.neg}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.sqrt}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.ceil}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.floor}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.trunc}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.nearest}` - -* New binary :ref:`vector instructions `: :math:`\K{v128.and}`, :math:`\K{v128.andnot}`, :math:`\K{v128.or}`, :math:`\K{v128.xor}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.add}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.sub}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.mul}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.add\_sat\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.sub\_sat\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.min\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.max\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.shl}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.shr\_}\sx`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.add}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.extmul\_}\half\K{\_i}\!N'\!\K{x}\!M'\!\K{\_}\sx`, :math:`\K{i16x8.q15mulr\_sat\_s}`, :math:`\K{i32x4.dot\_i16x8\_s}`, :math:`\K{i16x8.extadd\_pairwise\_i8x16\_}\sx`, :math:`\K{i32x4.extadd\_pairwise\_i16x8\_}\sx`, :math:`\K{i8x16.avgr\_u}`, :math:`\K{i16x8.avgr\_u}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.sub}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.mul}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.div}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.min}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.max}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.pmin}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.pmax}` - -* New ternary :ref:`vector instruction `: :math:`\K{v128.bitselect}` - -* New test :ref:`vector instructions `: :math:`\K{v128.any\_true}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.all\_true}` - -* New relational :ref:`vector instructions `: :math:`\K{i}\!N\!\K{x}\!M\!\K{.eq}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.ne}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.lt\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.gt\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.le\_}\sx`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.ge\_}\sx`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.eq}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.ne}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.lt}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.gt}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.le}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.ge}` - -* New conversion :ref:`vector instructions `::math:`\K{i32x4.trunc\_sat\_f32x4\_}\sx`, :math:`\K{i32x4.trunc\_sat\_f64x2\_}\sx\K{\_zero}`, :math:`\K{f32x4.convert\_i32x4\_}\sx`, :math:`\K{f32x4.demote\_f64x2\_zero}`, :math:`\K{f64x2.convert\_low\_i32x4\_}\sx`, :math:`\K{f64x2.promote\_low\_f32x4}` - -* New lane access :ref:`vector instructions `: :math:`\K{i}\!N\!\K{x}\!M\!\K{.extract\_lane\_}\sx^?`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.replace\_lane}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.extract\_lane}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.replace\_lane}` - -* New lane splitting/combining :ref:`vector instructions `: :math:`\K{i}\!N\!\K{x}\!M\!\K{.extend\_}\half\K{\_i}\!N'\!\K{x}\!M'\!\K{\_}\sx`, :math:`\K{i8x16.narrow\_i16x8\_}\sx`, :math:`\K{i16x8.narrow\_i32x4\_}\sx` - -* New byte reordering :ref:`vector instructions `: :math:`\K{i8x16.shuffle}`, :math:`\K{i8x16.swizzle}` - -* New injection/projection :ref:`vector instructions `: :math:`\K{i}\!N\!\K{x}\!M\!\K{.splat}`, :math:`\K{f}\!N\!\K{x}\!M\!\K{.splat}`, :math:`\K{i}\!N\!\K{x}\!M\!\K{.bitmask}` +Added vector type and instructions that manipulate multiple numeric values in parallel +(also known as *SIMD*, single instruction multiple data) [#proposal-vectype]_ + +* New :ref:`value type `: + + - |V128| + +* New :ref:`memory instructions `: + + - :math:`\K{v128.}\LOAD` + - :math:`\K{v128.}\LOAD{}\!N\!\K{x}\!M\!\K{\_}\sx` + - :math:`\K{v128.}\LOAD{}N\K{\_zero}` + - :math:`\K{v128.}\LOAD{}N\K{\_splat}` + - :math:`\K{v128.}\LOAD{}N\K{\_lane}` + - :math:`\K{v128.}\STORE` + - :math:`\K{v128.}\STORE{}N\K{\_lane}` + +* New constant :ref:`vector instruction `: + + - :math:`\K{v128.}\VCONST` + +* New unary :ref:`vector instructions `: + + - :math:`\K{v128.not}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.abs}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.neg}` + - :math:`\K{i8x16.popcnt}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.abs}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.neg}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.sqrt}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.ceil}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.floor}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.trunc}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.nearest}` + +* New binary :ref:`vector instructions `: + + - :math:`\K{v128.and}` + - :math:`\K{v128.andnot}` + - :math:`\K{v128.or}` + - :math:`\K{v128.xor}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.add}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.sub}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.mul}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.add\_sat\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.sub\_sat\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.min\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.max\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.shl}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.shr\_}\sx` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.add}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.sub}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.mul}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.div}` + - :math:`\K{i16x8.extadd\_pairwise\_i8x16\_}\sx` + - :math:`\K{i32x4.extadd\_pairwise\_i16x8\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.extmul\_}\half\K{\_i}\!N'\!\K{x}\!M'\!\K{\_}\sx` + - :math:`\K{i16x8.q15mulr\_sat\_s}` + - :math:`\K{i32x4.dot\_i16x8\_s}` + - :math:`\K{i8x16.avgr\_u}` + - :math:`\K{i16x8.avgr\_u}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.min}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.max}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.pmin}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.pmax}` + +* New ternary :ref:`vector instruction `: + + - :math:`\K{v128.bitselect}` + +* New test :ref:`vector instructions `: + + - :math:`\K{v128.any\_true}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.all\_true}` + +* New relational :ref:`vector instructions `: + + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.eq}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.ne}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.lt\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.gt\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.le\_}\sx` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.ge\_}\sx` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.eq}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.ne}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.lt}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.gt}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.le}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.ge}` + +* New conversion :ref:`vector instructions `: + + - :math:`\K{i32x4.trunc\_sat\_f32x4\_}\sx` + - :math:`\K{i32x4.trunc\_sat\_f64x2\_}\sx\K{\_zero}` + - :math:`\K{f32x4.convert\_i32x4\_}\sx` + - :math:`\K{f32x4.demote\_f64x2\_zero}` + - :math:`\K{f64x2.convert\_low\_i32x4\_}\sx` + - :math:`\K{f64x2.promote\_low\_f32x4}` + +* New lane access :ref:`vector instructions `: + + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.extract\_lane\_}\sx^?` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.replace\_lane}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.extract\_lane}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.replace\_lane}` + +* New lane splitting/combining :ref:`vector instructions `: + + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.extend\_}\half\K{\_i}\!N'\!\K{x}\!M'\!\K{\_}\sx` + - :math:`\K{i8x16.narrow\_i16x8\_}\sx` + - :math:`\K{i16x8.narrow\_i32x4\_}\sx` + +* New byte reordering :ref:`vector instructions `: + + - :math:`\K{i8x16.shuffle}` + - :math:`\K{i8x16.swizzle}` + +* New injection/projection :ref:`vector instructions `: + + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.splat}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.splat}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.bitmask}` .. [#proposal-signext] @@ -160,14 +293,138 @@ Added vector type and instructions that manipulate multiple numeric values in pa Release 3.0 ~~~~~~~~~~~ -.. index: instruction, function, call +.. index:: instruction, expression, constant + +Extended Constant Expressions +............................. + +Allowed basic numeric computations in constant expressions. [#proposal-extconst]_ + +* Extended set of :ref:`constant instructions ` with: + + - :math:`\K{i}\X{nn}\K{.add}` + - :math:`\K{i}\X{nn}\K{.sub}` + - :math:`\K{i}\X{nn}\K{.mul}` + - |GLOBALGET| for any previously declared immutable :ref:`global ` + +.. note:: + The :ref:`garbage collection ` extension added further constant instructions. + + +.. index:: instruction, function, call Tail Calls .......... -Added instructions to perform tail calls [#proposal-tailcall]_. +Added instructions to perform tail calls. [#proposal-tailcall]_ + +* New :ref:`control instructions `: + + - |RETURNCALL| + - |RETURNCALLINDIRECT| + + +.. index:: instruction, exception, reference type, tag type, tag, handler + +Exception Handling +.................. + +Added tag definitions, imports, and exports, and instructions to throw and catch exceptions [#proposal-exn]_ + +* :ref:`Modules ` may + + - :ref:`define ` tags + - :ref:`import ` tags + - :ref:`export ` tags + +* New :ref:`heap types `: + + - |EXN| + - |NOEXN| + +* New :ref:`reference type ` short-hands: + + - |EXNREF| + - |NULLEXNREF| + +* New :ref:`control instructions `: + + - |THROW| + - |THROWREF| + - |TRYTABLE| + +* New :ref:`tag section ` in binary format. + + +.. index:: instruction, memory, memory index, data segment, import, export + +Multiple Memories +................. + +Added the ability to use multiple memories per module. [#proposal-multimem]_ -* New :ref:`control instructions `: :math:`RETURNCALL` and :math:`RETURNCALLINDIRECT` +* :ref:`Modules ` may + + - :ref:`define ` multiple memories + - :ref:`import ` multiple memories + - :ref:`export ` multiple memories + +* :ref:`Memory instructions ` take a :ref:`memory index ` immediate: + + - |MEMORYSIZE| + - |MEMORYGROW| + - |MEMORYFILL| + - |MEMORYCOPY| + - |MEMORYINIT| + - :math:`t\K{.load}` + - :math:`t\K{.store}` + - :math:`t\K{.load}\!N\!\K{\_}\sx` + - :math:`t\K{.store}\!N` + - :math:`\K{v128.load}\!N\!\K{x}\!M\!\K{\_}\sx` + - :math:`\K{v128.load}\!N\!\K{\_zero}` + - :math:`\K{v128.load}\!N\!\K{\_splat}` + - :math:`\K{v128.load}\!N\!\K{\_lane}` + - :math:`\K{v128.store}\!N\!\K{\_lane}` + +* :ref:`Data segments ` take a :ref:`memory index ` + + +.. index:: address type, number type, table, memory, instruction + +64-bit Address Space +.................... + +Added the ability to declare an :math:`\I64` :ref:`address type ` for :ref:`tables ` and :ref:`memories `. [#proposal-addr64]_ + +* :ref:`Address types ` denote a subset of the integral :ref:`number types ` + +* :ref:`Table types ` include an :ref:`address type ` + +* :ref:`Memory types ` include an :ref:`address type ` + +* Operand types of :ref:`table ` and :ref:`memory ` instructions now depend on the subject's declared address type: + + - |TABLEGET| + - |TABLESET| + - |TABLESIZE| + - |TABLEGROW| + - |TABLEFILL| + - |TABLECOPY| + - |TABLEINIT| + - |MEMORYSIZE| + - |MEMORYGROW| + - |MEMORYFILL| + - |MEMORYCOPY| + - |MEMORYINIT| + - :math:`t\K{.load}` + - :math:`t\K{.store}` + - :math:`t\K{.load}\!N\!\K{\_}\sx` + - :math:`t\K{.store}\!N` + - :math:`\K{v128.load}\!N\!\K{x}\!M\!\K{\_}\sx` + - :math:`\K{v128.load}\!N\!\K{\_zero}` + - :math:`\K{v128.load}\!N\!\K{\_splat}` + - :math:`\K{v128.load}\!N\!\K{\_lane}` + - :math:`\K{v128.store}\!N\!\K{\_lane}` .. index:: reference, reference type, heap type, value type, local, local type, instruction, instruction type, table, function, function type, matching, subtyping @@ -175,114 +432,220 @@ Added instructions to perform tail calls [#proposal-tailcall]_. Typeful References .................. -Added more precise types for references [#proposal-typedref]_. +Added more precise types for references. [#proposal-typedref]_ + +* New generalised form of :ref:`reference types `: -* New generalised form of :ref:`reference types `: :math:`(\REF~\NULL^?~\heaptype)` + - :math:`(\REF~\NULL^?~\heaptype)` -* New class of :ref:`heap types `: |FUNC|, |EXTERN|, :math:`\typeidx` +* New class of :ref:`heap types `: + + - |FUNC| + - |EXTERN| + - :math:`\typeidx` * Basic :ref:`subtyping ` on :ref:`reference ` and :ref:`value ` types -* New :ref:`reference instructions `: |REFASNONNULL|, |BRONNULL|, |BRONNONNULL| +* New :ref:`reference instructions `: + + - |REFASNONNULL| + - |BRONNULL| + - |BRONNONNULL| + +* New :ref:`control instruction `: + + - |CALLREF| -* New :ref:`control instruction `: |CALLREF| +* Refined typing of :ref:`reference instruction `: -* Refined typing of :ref:`reference instruction ` |REFFUNC| with more precise result type + - |REFFUNC| with more precise result type * Refined typing of :ref:`local instructions ` and :ref:`instruction sequences ` to track the :ref:`initialization status ` of :ref:`locals ` with non-:ref:`defaultable ` type +* Refined decoding of :ref:`active ` :ref:`element segments ` with implicit element type and plain function indices (opcode :math:`0`) to produce :ref:`non-nullable ` :ref:`reference type `. + * Extended :ref:`table definitions ` with optional initializer expression .. index:: reference, reference type, heap type, field type, storage type, structure type, array type, composite type, sub type, recursive type +.. _extension-gc: Garbage Collection .................. -Added managed reference types [#proposal-gc]_. +Added managed reference types. [#proposal-gc]_ -* New forms of :ref:`heap types `: |ANY|, |EQT|, |I31|, |STRUCT|, |ARRAY|, |NONE|, |NOFUNC|, |NOEXTERN| +* New forms of :ref:`heap types `: -* New :ref:`reference type ` short-hands: |ANYREF|, |EQREF|, |I31REF|, |STRUCTREF|, |ARRAYREF|, |NULLREF|, |NULLFUNCREF|, |NULLEXTERNREF| + - |ANY| + - |EQT| + - |I31| + - |STRUCT| + - |ARRAY| + - |NONE| + - |NOFUNC| + - |NOEXTERN| -* New forms of type definitions: :ref:`structure ` and :ref:`array types `, :ref:`sub types `, and :ref:`recursive types ` +* New :ref:`reference type ` short-hands: + + - |ANYREF| + - |EQREF| + - |I31REF| + - |STRUCTREF| + - |ARRAYREF| + - |NULLREF| + - |NULLFUNCREF| + - |NULLEXTERNREF| + +* New forms of type definitions: + + - :ref:`structure ` + - :ref:`array types ` + - :ref:`sub types ` + - :ref:`recursive types ` * Enriched :ref:`subtyping ` based on explicitly declared :ref:`sub types ` and the new heap types -* New generic :ref:`reference instructions `: |REFEQ|, |REFTEST|, |REFCAST|, |BRONCAST|, |BRONCASTFAIL| +* New generic :ref:`reference instructions `: -* New :ref:`reference instructions ` for :ref:`unboxed scalars `: |REFI31|, :math:`\I31GET\K{\_}\sx` + - |REFEQ| + - |REFTEST| + - |REFCAST| + - |BRONCAST| + - |BRONCASTFAIL| -* New :ref:`reference instructions ` for :ref:`structure types `: |STRUCTNEW|, |STRUCTNEWDEFAULT|, :math:`\STRUCTGET\K{\_}\sx^?`, |STRUCTSET| +* New :ref:`reference instructions ` for :ref:`unboxed scalars `: -* New :ref:`reference instructions ` for :ref:`array types `: |ARRAYNEW|, |ARRAYNEWDEFAULT|, |ARRAYNEWFIXED|, |ARRAYNEWDATA|, |ARRAYNEWELEM|, :math:`\ARRAYGET\K{\_}\sx^?`, |ARRAYSET|, |ARRAYLEN|, |ARRAYFILL|, |ARRAYCOPY|, |ARRAYINITDATA|, |ARRAYINITELEM| + - |REFI31| + - :math:`\I31GET\K{\_}\sx` -* New :ref:`reference instructions ` for converting :ref:`host types `: |ANYCONVERTEXTERN|, |EXTERNCONVERTANY| +* New :ref:`reference instructions ` for :ref:`structure types `: -* Extended set of :ref:`constant instructions ` with |REFI31|, |STRUCTNEW|, |STRUCTNEWDEFAULT|, |ARRAYNEW|, |ARRAYNEWDEFAULT|, |ARRAYNEWFIXED|, |ANYCONVERTEXTERN|, |EXTERNCONVERTANY|, and |GLOBALGET| for any previously declared immutable :ref:`global ` + - |STRUCTNEW| + - |STRUCTNEWDEFAULT| + - :math:`\STRUCTGET\K{\_}\sx^?` + - |STRUCTSET| +* New :ref:`reference instructions ` for :ref:`array types `: -.. index:: instruction, exception, reference type, tag type, tag, handler + - |ARRAYNEW| + - |ARRAYNEWDEFAULT| + - |ARRAYNEWFIXED| + - |ARRAYNEWDATA| + - |ARRAYNEWELEM| + - :math:`\ARRAYGET\K{\_}\sx^?` + - |ARRAYSET| + - |ARRAYLEN| + - |ARRAYFILL| + - |ARRAYCOPY| + - |ARRAYINITDATA| + - |ARRAYINITELEM| -Exception Handling -.................. +* New :ref:`reference instructions ` for converting :ref:`external types `: -Added tag definitions, imports, and exports, and instructions to throw and catch exceptions [#proposal-exn]_ + - |ANYCONVERTEXTERN| + - |EXTERNCONVERTANY| -* Modules may :ref:`define `, :ref:`import `, and :ref:`export ` tags. +* Extended set of :ref:`constant instructions ` with: -* New :ref:`heap types `: |EXN|, |NOEXN| + - |REFI31| + - |STRUCTNEW| + - |STRUCTNEWDEFAULT| + - |ARRAYNEW| + - |ARRAYNEWDEFAULT| + - |ARRAYNEWFIXED| + - |ANYCONVERTEXTERN| + - |EXTERNCONVERTANY| -* New :ref:`reference type ` short-hands: |EXNREF|, |NULLEXNREF| -* New :ref:`control instructions `: |THROW|, |THROWREF|, and |TRYTABLE|. +.. index:: instruction, vector instruction, SIMD -* New :ref:`tag section ` in binary format. +Relaxed Vector Instructions +........................... +Added new *relaxed* vector instructions, +whose behaviour is non-deterministic and implementation-dependent. [#proposal-relaxed]_ -.. index:: reference, reference type, heap type, field type, storage type, structure type, array type, composite type, sub type, recursive type -.. _extension-gc: +* New binary :ref:`vector instruction `: -Garbage collection -.................. + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.relaxed\_min}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.relaxed\_max}` + - :math:`\K{i16x8.relaxed\_q15mulr\_s}` + - :math:`\K{i16x8.relaxed\_dot\_i8x16\_i7x16\_s}` -Added managed reference types. [#proposal-gc]_ +* New ternary :ref:`vector instruction `: -* New forms of :ref:`heap types `: |ANY|, |EQT|, |I31|, |STRUCT|, |ARRAY|, |NONE|, |NOFUNC|, |NOEXTERN| + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.relaxed\_madd}` + - :math:`\K{f}\!N\!\K{x}\!M\!\K{.relaxed\_nmadd}` + - :math:`\K{i}\!N\!\K{x}\!M\!\K{.relaxed\_laneselect}` + - :math:`\K{i32x4.relaxed\_dot\_i8x16\_i7x16\_add\_s}` -* New :ref:`reference type ` short-hands: |ANYREF|, |EQREF|, |I31REF|, |STRUCTREF|, |ARRAYREF|, |NULLREF|, |NULLFUNCREF|, |NULLEXTERNREF| +* New conversion :ref:`vector instructions `: -* New forms of type definitions: :ref:`structure ` and :ref:`array types `, :ref:`sub types `, and :ref:`recursive types ` + - :math:`\K{i32x4.relaxed\_trunc\_f32x4\_}\sx` + - :math:`\K{i32x4.relaxed\_trunc\_f64x2\_}\sx\K{\_zero}` -* Enriched :ref:`subtyping ` based on explicitly declared :ref:`sub types ` and the new heap types +* New byte reordering :ref:`vector instruction `: + + - :math:`\K{i8x16.relaxed\_swizzle}` + + +.. index:: determinism, non-determinism, profiles -* New generic :ref:`reference instructions `: |REFEQ|, |REFTEST|, |REFCAST|, |BRONCAST|, |BRONCASTFAIL| +Profiles +........ -* New :ref:`reference instructions ` for :ref:`unboxed scalars `: |REFI31|, :math:`\I31GET\K{\_}\sx` +Introduced the concept of :ref:`profile ` for specifying language subsets. -* New :ref:`reference instructions ` for :ref:`structure types `: |STRUCTNEW|, |STRUCTNEWDEFAULT|, :math:`\STRUCTGET\K{\_}\sx^?`, |STRUCTSET| +* A new profile defining a :ref:`deterministic ` mode of execution. -* New :ref:`reference instructions ` for :ref:`array types `: |ARRAYNEW|, |ARRAYNEWDEFAULT|, |ARRAYNEWFIXED|, |ARRAYNEWDATA|, |ARRAYNEWELEM|, :math:`\ARRAYGET\K{\_}\sx^?`, |ARRAYSET|, |ARRAYLEN|, |ARRAYFILL|, |ARRAYCOPY|, |ARRAYINITDATA|, |ARRAYINITELEM| -* New :ref:`reference instructions ` for converting :ref:`host types `: |ANYCONVERTEXTERN|, |EXTERNCONVERTANY| +.. index:: text format, annotation, custom section, identifier, module, type, function, local, structure field + +Custom Annotations +.................. + +Added generic syntax for custom annotations in the text format, +mirroring the role of custom sections in the binary format. [#proposal-annot]_ -* Extended set of :ref:`constant instructions ` with |REFI31|, |STRUCTNEW|, |STRUCTNEWDEFAULT|, |ARRAYNEW|, |ARRAYNEWDEFAULT|, |ARRAYNEWFIXED|, |ANYCONVERTEXTERN|, |EXTERNCONVERTANY| +* :ref:`Annotations ` of the form :math:`\text{(@id~\dots)}` are allowed anywhere in the :ref:`text format ` + +* :ref:`Identifiers ` can be escaped as :math:`\text{@"\dots"}` with arbitrary :ref:`names ` + +* Defined :ref:`name annotations ` :math:`\text{(@name~"\dots")}` for: + + - :ref:`module names ` + - :ref:`type names ` + - :ref:`function names ` + - :ref:`local names ` + - :ref:`field names ` + +* Defined :ref:`custom annotation ` :math:`\text{(@custom~"\dots")}` to represent arbitrary :ref:`custom sections ` in the text format .. [#proposal-extconst] - https://github.com/WebAssembly/extended-const/blob/main/proposals/extended-const/ + https://github.com/WebAssembly/spec/tree/main/proposals/extended-const/ .. [#proposal-tailcall] https://github.com/WebAssembly/spec/tree/main/proposals/tail-call/ +.. [#proposal-exn] + https://github.com/WebAssembly/spec/tree/main/proposals/exception-handling/ + .. [#proposal-multimem] - https://github.com/WebAssembly/multi-memory/blob/main/proposals/multi-memory/ + https://github.com/WebAssembly/spec/tree/main/proposals/multi-memory/ + +.. [#proposal-addr64] + https://github.com/WebAssembly/spec/tree/main/proposals/memory64/ .. [#proposal-typedref] https://github.com/WebAssembly/spec/tree/main/proposals/function-references/ -.. [#proposal-exn] - https://github.com/WebAssembly/spec/tree/main/proposals/exception-handling/ - .. [#proposal-gc] https://github.com/WebAssembly/spec/tree/main/proposals/gc/ + +.. [#proposal-relaxed] + https://github.com/WebAssembly/spec/tree/main/proposals/relaxed-simd/ + +.. [#proposal-annot] + https://github.com/WebAssembly/annotations/tree/main/proposals/annotations/ diff --git a/document/core/appendix/custom.rst b/document/core/appendix/custom.rst index dcafe0079..96c9033ef 100644 --- a/document/core/appendix/custom.rst +++ b/document/core/appendix/custom.rst @@ -1,13 +1,12 @@ -.. index:: custom section, section, binary format +.. index:: custom section, section, binary format, annotation, text format -Custom Sections ---------------- +Custom Sections and Annotations +------------------------------- -This appendix defines dedicated :ref:`custom sections ` for WebAssembly's :ref:`binary format `. -Such sections do not contribute to, or otherwise affect, the WebAssembly semantics, and like any custom section they may be ignored by an implementation. +This appendix defines dedicated :ref:`custom sections ` for WebAssembly's :ref:`binary format ` and :ref:`annotations ` for the text format. +Such sections or annotations do not contribute to, or otherwise affect, the WebAssembly semantics, and may be ignored by an implementation. However, they provide useful meta data that implementations can make use of to improve user experience or take compilation hints. -Currently, only one dedicated custom section is defined, the :ref:`name section`. .. index:: ! name section, name, Unicode UTF-8 @@ -47,6 +46,8 @@ Each subsection consists of a \Bmodulenamesubsec^? \\ &&& \Bfuncnamesubsec^? \\ &&& \Blocalnamesubsec^? \\ &&& + \Btypenamesubsec^? \\ &&& + \Bfieldnamesubsec^? \\ &&& \Btagnamesubsec^? \\ \production{name subsection} & \Bnamesubsection_N(\B{B}) &::=& N{:}\Bbyte~~\X{size}{:}\Bu32~~\B{B} @@ -153,7 +154,7 @@ It consists of an :ref:`indirect name map ` assigning lo .. _binary-typenamesec: Type Names -.............. +.......... The *type name subsection* has the id 4. It consists of a :ref:`name map ` assigning type names to :ref:`type indices `. @@ -161,7 +162,7 @@ It consists of a :ref:`name map ` assigning type names to :ref:` .. math:: \begin{array}{llclll} \production{type name subsection} & \Btypenamesubsec &::=& - \Bnamesubsection_1(\Bnamemap) \\ + \Bnamesubsection_4(\Bnamemap) \\ \end{array} @@ -172,12 +173,12 @@ Field Names ........... The *field name subsection* has the id 10. -It consists of an :ref:`indirect name map ` assigning field names to :ref:`field indices ` grouped by :ref:`type indices `. +It consists of an :ref:`indirect name map ` assigning field names to :ref:`field indices ` grouped by the :ref:`type indices ` of their respective :ref:`structure types `. .. math:: \begin{array}{llclll} \production{field name subsection} & \Bfieldnamesubsec &::=& - \Bnamesubsection_2(\Bindirectnamemap) \\ + \Bnamesubsection_10(\Bindirectnamemap) \\ \end{array} @@ -195,3 +196,249 @@ It consists of a :ref:`name map ` assigning tag names to :ref:`t \production{tag name subsection} & \Btagnamesubsec &::=& \Bnamesubsection_1(\Bnamemap) \\ \end{array} + + +.. index:: ! name annotation, name, Unicode UTF-8 +.. _text-nameannot: + +Name Annotations +~~~~~~~~~~~~~~~~ + +*Name annotations* are the textual analogue to the :ref:`name section ` and provide a textual representation for it. +Consequently, their id is :math:`\T{@name}`. + +Analogous to the name section, name annotations are allowed on :ref:`modules `, :ref:`functions `, and :ref:`locals ` (including :ref:`parameters `). +They can be placed where the text format allows binding occurrences of respective :ref:`identifiers `. +If both an identifier and a name annotation are given, the annotation is expected *after* the identifier. +In that case, the annotation takes precedence over the identifier as a textual representation of the binding's name. +At most one name annotation may be given per binding. + +All name annotations have the following format: + +.. math:: + \begin{array}{llclll} + \production{name annotation} & \Tnameannot &::=& + \text{(@name}~\Tstring~\text{)} \\ + \end{array} + + +.. note:: + All name annotations can be arbitrary UTF-8 :ref:`strings `. + Names need not be unique. + + +.. index:: module +.. _text-modulenameannot: + +Module Names +............ + +A *module name annotation* must be placed on a :ref:`module ` definition, +directly after the :math:`\text{module}` keyword, or if present, after the following module :ref:`identifier `. + +.. math:: + \begin{array}{llclll} + \production{module name annotation} & \Tmodulenameannot &::=& + \Tnameannot \\ + \end{array} + + +.. index:: function +.. _text-funcnameannot: + +Function Names +.............. + +A *function name annotation* must be placed on a :ref:`function ` definition or function :ref:`import `, +directly after the :math:`\text{func}` keyword, or if present, after the following function :ref:`identifier ` or. + +.. math:: + \begin{array}{llclll} + \production{function name annotation} & \Tfuncnameannot &::=& + \Tnameannot \\ + \end{array} + + +.. index:: function, parameter +.. _text-paramnameannot: + +Parameter Names +............... + +A *parameter name annotation* must be placed on a :ref:`parameter ` declaration, +directly after the :math:`\text{param}` keyword, or if present, after the following parameter :ref:`identifier `. +It may only be placed on a declaration that declares exactly one parameter. + +.. math:: + \begin{array}{llclll} + \production{parameter name annotation} & \Tparamnameannot &::=& + \Tnameannot \\ + \end{array} + + +.. index:: function, local +.. _text-localnameannot: + +Local Names +........... + +A *local name annotation* must be placed on a :ref:`local ` declaration, +directly after the :math:`\text{local}` keyword, or if present, after the following local :ref:`identifier `. +It may only be placed on a declaration that declares exactly one local. + +.. math:: + \begin{array}{llclll} + \production{local name annotation} & \Tlocalnameannot &::=& + \Tnameannot \\ + \end{array} + + +.. index:: type +.. _text-typenameannot: + +Type Names +.......... + +A *type name annotation* must be placed on a :ref:`type ` declaration, +directly after the :math:`\text{type}` keyword, or if present, after the following type :ref:`identifier `. + +.. math:: + \begin{array}{llclll} + \production{type name annotation} & \Ttypenameannot &::=& + \Tnameannot \\ + \end{array} + + +.. index:: type, structure type, field +.. _text-fieldnameannot: + +Field Names +........... + +A *field name annotation* must be placed on the field of a :ref:`structure type `, +directly after the :math:`\text{field}` keyword, or if present, after the following field :ref:`identifier `. +It may only be placed on a declaration that declares exactly one field. + +.. math:: + \begin{array}{llclll} + \production{field name annotation} & \Tfieldnameannot &::=& + \Tnameannot \\ + \end{array} + + +.. index:: tag +.. _text-tagnameannot: + +Tag Names +......... + +A *tag name annotation* must be placed on a :ref:`tag declaration ` or tag :ref:`import `, +directly after the :math:`\text{tag}` keyword, or if present, after the following tag :ref:`identifier `. + +.. math:: + \begin{array}{llclll} + \production{tag name annotation} & \Ttagnameannot &::=& + \Tnameannot \\ + \end{array} + + +.. index:: ! custom annotation, custom section +.. _text-customannot: + +Custom Annotations +~~~~~~~~~~~~~~~~~~ + +*Custom annotations* are a generic textual representation for any :ref:`custom section `. +Their id is :math:`\T{@custom}`. +By generating custom annotations, tools converting between :ref:`binary format ` and :ref:`text format ` can maintain and round-trip the content of custom sections even when they do not recognize them. + +Custom annotations must be placed inside a :ref:`module ` definition. +They must occur anywhere after the :math:`\text{module}` keyword, or if present, after the following module :ref:`identifier `. +They must not be nested into other constructs. + +.. math:: + \begin{array}{llclll} + \production{custom annotation} & \Tcustomannot &::=& + \text{(@custom}~~\Tstring~~\Tcustomplace^?~~\Tdatastring~~\text{)} \\ + \production{custom placement} & \Tcustomplace &::=& + \text{(}~\text{before}~~\text{first}~\text{)} \\ &&|& + \text{(}~\text{before}~~\Tsec~\text{)} \\ &&|& + \text{(}~\text{after}~~\Tsec~\text{)} \\ &&|& + \text{(}~\text{after}~~\text{last}~\text{)} \\ + \production{section} & \Tsec &::=& + \text{type} \\ &&|& + \text{import} \\ &&|& + \text{func} \\ &&|& + \text{table} \\ &&|& + \text{memory} \\ &&|& + \text{global} \\ &&|& + \text{export} \\ &&|& + \text{start} \\ &&|& + \text{elem} \\ &&|& + \text{code} \\ &&|& + \text{data} \\ &&|& + \text{datacount} \\ + \end{array} + +The first :ref:`string ` in a custom annotation denotes the name of the custom section it represents. +The remaining strings collectively represent the section's payload data, written as a :ref:`data string `, which can be split up into a possibly empty sequence of individual string literals (similar to :ref:`data segments `). + +An arbitrary number of custom annotations (even of the same name) may occur in a module, +each defining a separate custom section when converting to :ref:`binary format `. +Placement of the sections in the binary can be customized via explicit *placement* directives, that position them either directly before or directly after a known section. +That section must exist and be non-empty in the binary encoding of the annotated module. +The placements :math:`\T{(before~first)}` and :math:`\T{(after~last)}` denote virtual sections before the first and after the last known section, respectively. +When the placement directive is omitted, it defaults to :math:`\T{(after~last)}`. + +If multiple placement directives appear for the same position, then the sections are all placed there, in order of their appearance in the text. +For this purpose, the position :math:`\T{after}` a section is considered different from the position :math:`\T{before}` the consecutive section, and the former occurs before the latter. + +.. note:: + Future versions of WebAssembly may introduce additional sections between others or at the beginning or end of a module. + Using :math:`\T{first}` and :math:`\T{last}` guarantees that placement will still go before or after any future section, respectively. + +If a custom section with a specific section id is given as well as annotations representing the same custom section (e.g., :math:`\T{@name}` :ref:`annotations ` as well as a :math:`\T{@custom}` annotation for a :math:`\T{name}` :ref:`section `), then two sections are assumed to be created. +Their relative placement will depend on the placement directive given for the :math:`\T{@custom}` annotation as well as the implicit placement requirements of the custom section, which are applied to the other annotation. + +.. note:: + + For example, the following module, + + .. code-block:: none + + (module + (@custom "A" "aaa") + (type $t (func)) + (@custom "B" (after func) "bbb") + (@custom "C" (before func) "ccc") + (@custom "D" (after last) "ddd") + (table 10 funcref) + (func (type $t)) + (@custom "E" (after import) "eee") + (@custom "F" (before type) "fff") + (@custom "G" (after data) "ggg") + (@custom "H" (after code) "hhh") + (@custom "I" (after func) "iii") + (@custom "J" (before func) "jjj") + (@custom "K" (before first) "kkk") + ) + + will result in the following section ordering: + + .. code-block:: none + + custom section "K" + custom section "F" + type section + custom section "E" + custom section "C" + custom section "J" + function section + custom section "B" + custom section "I" + table section + code section + custom section "H" + custom section "G" + custom section "A" + custom section "D" diff --git a/document/core/appendix/embedding.rst b/document/core/appendix/embedding.rst index 4bf979dbf..d2d7c54d0 100644 --- a/document/core/appendix/embedding.rst +++ b/document/core/appendix/embedding.rst @@ -21,7 +21,7 @@ Types In the description of the embedder interface, syntactic classes from the :ref:`abstract syntax ` and the :ref:`runtime's abstract machine ` are used as names for variables that range over the possible objects from that class. Hence, these syntactic classes can also be interpreted as types. -For numeric parameters, notation like :math:`n:\u32` is used to specify a symbolic name in addition to the respective value range. +For numeric parameters, notation like :math:`i:\u64` is used to specify a symbolic name in addition to the respective value range. .. _embed-bool: @@ -39,10 +39,19 @@ Interface operation that are predicates return Boolean values: .. _embed-error: -Errors -~~~~~~ +Exceptions and Errors +~~~~~~~~~~~~~~~~~~~~~ + +Invoking an exported function may throw or propagate exceptions, expressed by an auxiliary syntactic class: + +.. math:: + \begin{array}{llll} + \production{exception} & \exception &::=& \ETHROW ~ \exnaddr \\ + \end{array} -Failure of an interface operation is indicated by an auxiliary syntactic class: +The exception address :math:`exnaddr` identifies the exception thrown. + +Failure of an interface operation is also indicated by an auxiliary syntactic class: .. math:: \begin{array}{llll} @@ -56,6 +65,8 @@ In addition to the error conditions specified explicitly in this section, such a Implementations can refine it to carry suitable classifications and diagnostic messages. + + Pre- and Post-Conditions ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -153,8 +164,8 @@ Modules .. index:: instantiation, module instance .. _embed-module-instantiate: -:math:`\F{module\_instantiate}(\store, \module, \externval^\ast) : (\store, \moduleinst ~|~ \error)` -.................................................................................................... +:math:`\F{module\_instantiate}(\store, \module, \externval^\ast) : (\store, \moduleinst ~|~ \exception ~|~ \error)` +................................................................................................................... 1. Try :ref:`instantiating ` :math:`\module` in :math:`\store` with :ref:`external values ` :math:`\externval^\ast` as imports: @@ -307,14 +318,16 @@ Functions .. index:: invocation, value, result .. _embed-func-invoke: -:math:`\F{func\_invoke}(\store, \funcaddr, \val^\ast) : (\store, \val^\ast ~|~ \error)` -........................................................................................ +:math:`\F{func\_invoke}(\store, \funcaddr, \val^\ast) : (\store, \val^\ast ~|~ \exception ~|~ \error)` +...................................................................................................... 1. Try :ref:`invoking ` the function :math:`\funcaddr` in :math:`\store` with :ref:`values ` :math:`\val^\ast` as arguments: a. If it succeeds with :ref:`values ` :math:`{\val'}^\ast` as results, then let :math:`\X{result}` be :math:`{\val'}^\ast`. - b. Else it has trapped, hence let :math:`\X{result}` be :math:`\ERROR`. + b. Else if the outcome is an exception with a thrown :ref:`exception ` :math:`\REFEXNADDR~\exnaddr` as the result, then let :math:`\X{result}` be :math:`\ETHROW~\exnaddr` + + c. Else it has trapped, hence let :math:`\X{result}` be :math:`\ERROR`. 2. Return the new store paired with :math:`\X{result}`. @@ -322,7 +335,8 @@ Functions ~ \\ \begin{array}{lclll} \F{func\_invoke}(S, a, v^\ast) &=& (S', {v'}^\ast) && (\iff \invoke(S, a, v^\ast) \stepto^\ast S'; F; {v'}^\ast) \\ - \F{func\_invoke}(S, a, v^\ast) &=& (S', \ERROR) && (\iff \invoke(S, a, v^\ast) \stepto^\ast S'; F; \result) \\ + \F{func\_invoke}(S, a, v^\ast) &=& (S', \ETHROW~a') && (\iff \invoke(S, a, v^\ast) \stepto^\ast S'; F; \XT[(\REFEXNADDR~a')~\THROWREF] \\ + \F{func\_invoke}(S, a, v^\ast) &=& (S', \ERROR) && (\iff \invoke(S, a, v^\ast) \stepto^\ast S'; F; \TRAP) \\ \end{array} .. note:: @@ -369,7 +383,7 @@ Tables .. _embed-table-read: -:math:`\F{table\_read}(\store, \tableaddr, i:\u32) : \reff ~|~ \error` +:math:`\F{table\_read}(\store, \tableaddr, i:\u64) : \reff ~|~ \error` ...................................................................... 1. Let :math:`\X{ti}` be the :ref:`table instance ` :math:`\store.\STABLES[\tableaddr]`. @@ -387,7 +401,7 @@ Tables .. _embed-table-write: -:math:`\F{table\_write}(\store, \tableaddr, i:\u32, \reff) : \store ~|~ \error` +:math:`\F{table\_write}(\store, \tableaddr, i:\u64, \reff) : \store ~|~ \error` ............................................................................... 1. Let :math:`\X{ti}` be the :ref:`table instance ` :math:`\store.\STABLES[\tableaddr]`. @@ -407,7 +421,7 @@ Tables .. _embed-table-size: -:math:`\F{table\_size}(\store, \tableaddr) : \u32` +:math:`\F{table\_size}(\store, \tableaddr) : \u64` .................................................. 1. Return the length of :math:`\store.\STABLES[\tableaddr].\TIELEM`. @@ -423,7 +437,7 @@ Tables .. _embed-table-grow: -:math:`\F{table\_grow}(\store, \tableaddr, n:\u32, \reff) : \store ~|~ \error` +:math:`\F{table\_grow}(\store, \tableaddr, n:\u64, \reff) : \store ~|~ \error` .............................................................................. 1. Try :ref:`growing ` the :ref:`table instance ` :math:`\store.\STABLES[\tableaddr]` by :math:`n` elements with initialization value :math:`\reff`: @@ -481,7 +495,7 @@ Memories .. _embed-mem-read: -:math:`\F{mem\_read}(\store, \memaddr, i:\u32) : \byte ~|~ \error` +:math:`\F{mem\_read}(\store, \memaddr, i:\u64) : \byte ~|~ \error` .................................................................. 1. Let :math:`\X{mi}` be the :ref:`memory instance ` :math:`\store.\SMEMS[\memaddr]`. @@ -499,12 +513,12 @@ Memories .. _embed-mem-write: -:math:`\F{mem\_write}(\store, \memaddr, i:\u32, \byte) : \store ~|~ \error` +:math:`\F{mem\_write}(\store, \memaddr, i:\u64, \byte) : \store ~|~ \error` ........................................................................... 1. Let :math:`\X{mi}` be the :ref:`memory instance ` :math:`\store.\SMEMS[\memaddr]`. -2. If :math:`\u32` is larger than or equal to the length of :math:`\X{mi}.\MIDATA`, then return :math:`\ERROR`. +2. If :math:`i` is larger than or equal to the length of :math:`\X{mi}.\MIDATA`, then return :math:`\ERROR`. 3. Replace :math:`\X{mi}.\MIDATA[i]` with :math:`\byte`. @@ -519,7 +533,7 @@ Memories .. _embed-mem-size: -:math:`\F{mem\_size}(\store, \memaddr) : \u32` +:math:`\F{mem\_size}(\store, \memaddr) : \u64` .............................................. 1. Return the length of :math:`\store.\SMEMS[\memaddr].\MIDATA` divided by the :ref:`page size `. @@ -535,7 +549,7 @@ Memories .. _embed-mem-grow: -:math:`\F{mem\_grow}(\store, \memaddr, n:\u32) : \store ~|~ \error` +:math:`\F{mem\_grow}(\store, \memaddr, n:\u64) : \store ~|~ \error` ................................................................... 1. Try :ref:`growing ` the :ref:`memory instance ` :math:`\store.\SMEMS[\memaddr]` by :math:`n` :ref:`pages `: @@ -576,6 +590,77 @@ Tags \end{array} +.. _embed-tag-type: + +:math:`\F{tag\_type}(\store, \tagaddr) : \tagtype` +.................................................. + +1. Return :math:`S.\STAGS[a].\TAGITYPE`. + +2. Post-condition: the returned :ref:`tag type ` is :ref:`valid `. + +.. math:: + \begin{array}{lclll} + \F{tag\_type}(S, a) &=& S.\STAGS[a].\TAGITYPE \\ + \end{array} + + +.. index:: exception, exception address, store, exception instance, exception type +.. _embed-exception: + +Exceptions +~~~~~~~~~~ + +.. _embed-exn-alloc: + +:math:`\F{exn\_alloc}(\store, \tagaddr, \val^\ast) : (\store, \exnaddr)` +........................................................................ + +1. Pre-condition: :math:`\tagaddr` is an allocated :ref:`tag address `. + +2. Let :math:`\exnaddr` be the result of :ref:`allocating an exception instance ` in :math:`\store` with :ref:`tag address ` :math:`\tagaddr` and initialization values :math:`\val^\ast`. + +3. Return the new store paired with :math:`\exnaddr`. + +.. math:: + \begin{array}{lcll} + \F{exn\_alloc}(S, \tagaddr, \val^\ast) &=& (S \compose \{\SEXNS~\exninst\}, |S.\SEXNS|) & + (\iff \exninst = \{\EITAG~\tagaddr, \EIFIELDS~\val^\ast\} \\ + \end{array} + + +.. _embed-exn-tag: + +:math:`\F{exn\_tag}(\store, \exnaddr) : \tagaddr` +................................................. + +1. Let :math:`\exninst` be the :ref:`exception instance ` :math:`\store.\SEXNS[\exnaddr]`. + +2. Return the :ref:`tag address ` :math:`\exninst.\EITAG`. + +.. math:: + \begin{array}{lcll} + \F{exn\_tag}(S, a) &=& \exninst.\EITAG & + (\iff \exninst = S.\SEXNS[a]) \\ + \end{array} + + +.. _embed-exn-read: + +:math:`\F{exn\_read}(\store, \exnaddr) : \val^\ast` +................................................... + +1. Let :math:`\exninst` be the :ref:`exception instance ` :math:`\store.\SEXNS[\exnaddr]`. + +2. Return the :ref:`values ` :math:`\exninst.\EIFIELDS`. + +.. math:: + \begin{array}{lcll} + \F{exn\_read}(S, a) &=& \exninst.\EIFIELDS & + (\iff \exninst = S.\SEXNS[a]) \\ + \end{array} + + .. index:: global, global address, store, global instance, global type, value .. _embed-global: @@ -652,11 +737,12 @@ Globals \end{array} -.. index:: reference, reference type +.. index:: reference, reference type, value type, value .. _embed-ref-type: +.. _embed-val-default: -References -~~~~~~~~~~ +Values +~~~~~~ :math:`\F{ref\_type}(\store, \reff) : \reftype` ............................................... @@ -678,6 +764,20 @@ References In such cases, this function may return a less precise supertype. +:math:`\F{val\_default}(\valtype) : \val` +............................................... + +1. If :math:`\default_{valtype}` is not defined, then return :math:`\ERROR`. + +1. Else, return the :ref:`value ` :math:`\default_{valtype}`. + +.. math:: + \begin{array}{lclll} + \F{val\_default}(t) &=& v && (\iff \default_t = v) \\ + \F{val\_default}(t) &=& \ERROR && (\iff \default_t = \epsilon) \\ + \end{array} + + .. index:: value type, external type, subtyping .. _embed-match-valtype: .. _embed-match-externtype: @@ -696,7 +796,7 @@ Matching .. math:: \begin{array}{lclll} - \F{match\_reftype}(t_1, t_2) &=& \TRUE && (\iff \vdashvaltypematch t_1 \matchesvaltype t_2) \\ + \F{match\_reftype}(t_1, t_2) &=& \TRUE && (\iff {} \vdashvaltypematch t_1 \matchesvaltype t_2) \\ \F{match\_reftype}(t_1, t_2) &=& \FALSE && (\otherwise) \\ \end{array} @@ -712,6 +812,6 @@ Matching .. math:: \begin{array}{lclll} - \F{match\_externtype}(\X{et}_1, \X{et}_2) &=& \TRUE && (\iff \vdashexterntypematch \X{et}_1 \matchesexterntype \X{et}_2) \\ + \F{match\_externtype}(\X{et}_1, \X{et}_2) &=& \TRUE && (\iff {} \vdashexterntypematch \X{et}_1 \matchesexterntype \X{et}_2) \\ \F{match\_externtype}(\X{et}_1, \X{et}_2) &=& \FALSE && (\otherwise) \\ \end{array} diff --git a/document/core/appendix/index-instructions.py b/document/core/appendix/index-instructions.py index e63d78faf..3a0a0e434 100755 --- a/document/core/appendix/index-instructions.py +++ b/document/core/appendix/index-instructions.py @@ -334,7 +334,7 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(r'\STRUCTGETS~x~y', r'\hex{FB}~\hex{03}', r'[(\REF~\NULL~x)] \to [\I32]', r'valid-struct.get', r'exec-struct.get'), Instruction(r'\STRUCTGETU~x~y', r'\hex{FB}~\hex{04}', r'[(\REF~\NULL~x)] \to [\I32]', r'valid-struct.get', r'exec-struct.get'), Instruction(r'\STRUCTSET~x~y', r'\hex{FB}~\hex{05}', r'[(\REF~\NULL~x)~t] \to []', r'valid-struct.set', r'exec-struct.set'), - Instruction(r'\ARRAYNEW~x', r'\hex{FB}~\hex{06}', r'[t] \to [(\REF~x)]', r'valid-array.new', r'exec-array.new'), + Instruction(r'\ARRAYNEW~x', r'\hex{FB}~\hex{06}', r'[t~\I32] \to [(\REF~x)]', r'valid-array.new', r'exec-array.new'), Instruction(r'\ARRAYNEWDEFAULT~x', r'\hex{FB}~\hex{07}', r'[\I32] \to [(\REF~x)]', r'valid-array.new', r'exec-array.new'), Instruction(r'\ARRAYNEWFIXED~x~n', r'\hex{FB}~\hex{08}', r'[t^n] \to [(\REF~x)]', r'valid-array.new_fixed', r'exec-array.new_fixed'), Instruction(r'\ARRAYNEWDATA~x~y', r'\hex{FB}~\hex{09}', r'[\I32~\I32] \to [(\REF~x)]', r'valid-array.new_data', r'exec-array.new_data'), @@ -349,14 +349,14 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(r'\ARRAYINITDATA~x~y', r'\hex{FB}~\hex{12}', r'[(\REF~\NULL~x)~\I32~\I32~\I32] \to []', r'valid-array.init_data', r'exec-array.init_data'), Instruction(r'\ARRAYINITELEM~x~y', r'\hex{FB}~\hex{13}', r'[(\REF~\NULL~x)~\I32~\I32~\I32] \to []', r'valid-array.init_elem', r'exec-array.init_elem'), Instruction(r'\REFTEST~(\REF~t)', r'\hex{FB}~\hex{14}', r"[(\REF~t')] \to [\I32]", r'valid-ref.test', r'exec-ref.test'), - Instruction(r'\REFTEST~(\REF~\NULL~t)', r'\hex{FB}~\hex{15}', r"[(REF~\NULL~t')] \to [\I32]", r'valid-ref.test', r'exec-ref.test'), + Instruction(r'\REFTEST~(\REF~\NULL~t)', r'\hex{FB}~\hex{15}', r"[(\REF~\NULL~t')] \to [\I32]", r'valid-ref.test', r'exec-ref.test'), Instruction(r'\REFCAST~(\REF~t)', r'\hex{FB}~\hex{16}', r"[(\REF~t')] \to [(\REF~t)]", r'valid-ref.cast', r'exec-ref.cast'), Instruction(r'\REFCAST~(\REF~\NULL~t)', r'\hex{FB}~\hex{17}', r"[(\REF~\NULL~t')] \to [(\REF~\NULL~t)]", r'valid-ref.cast', r'exec-ref.cast'), Instruction(r'\BRONCAST~t_1~t_2', r'\hex{FB}~\hex{18}', r'[t_1] \to [t_1\reftypediff t_2]', r'valid-br_on_cast', r'exec-br_on_cast'), Instruction(r'\BRONCASTFAIL~t_1~t_2', r'\hex{FB}~\hex{19}', r'[t_1] \to [t_2]', r'valid-br_on_cast_fail', r'exec-br_on_cast_fail'), Instruction(r'\ANYCONVERTEXTERN', r'\hex{FB}~\hex{1A}', r'[(\REF~\NULL~\EXTERN)] \to [(\REF~\NULL~\ANY)]', r'valid-any.convert_extern', r'exec-any.convert_extern'), Instruction(r'\EXTERNCONVERTANY', r'\hex{FB}~\hex{1B}', r'[(\REF~\NULL~\ANY)] \to [(\REF~\NULL~\EXTERN)]', r'valid-extern.convert_any', r'exec-extern.convert_any'), - Instruction(r'\REFI31', r'\hex{FB}~\hex{1C}', r'[\I32] \to [\I31REF]', r'valid-ref.i31', r'exec-ref.i31'), + Instruction(r'\REFI31', r'\hex{FB}~\hex{1C}', r'[\I32] \to [(\REF~\I31)]', r'valid-ref.i31', r'exec-ref.i31'), Instruction(r'\I31GETS', r'\hex{FB}~\hex{1D}', r'[\I31REF] \to [\I32]', r'valid-i31.get_sx', r'exec-i31.get_sx'), Instruction(r'\I31GETU', r'\hex{FB}~\hex{1E}', r'[\I31REF] \to [\I32]', r'valid-i31.get_sx', r'exec-i31.get_sx'), Instruction(None, r'\hex{FB}~\hex{1E} \dots'), @@ -633,7 +633,26 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(r'\I32X4.\VTRUNC\K{\_sat\_f64x2\_u\_zero}', r'\hex{FD}~~\hex{FD}~~\hex{01}', r'[\V128] \to [\V128]', r'valid-vcvtop', r'exec-vcvtop', r'op-trunc_sat_u'), Instruction(r'\F64X2.\VCONVERT\K{\_low\_i32x4\_s}', r'\hex{FD}~~\hex{FE}~~\hex{01}', r'[\V128] \to [\V128]', r'valid-vcvtop', r'exec-vcvtop', r'op-convert_s'), Instruction(r'\F64X2.\VCONVERT\K{\_low\_i32x4\_u}', r'\hex{FD}~~\hex{FF}~~\hex{01}', r'[\V128] \to [\V128]', r'valid-vcvtop', r'exec-vcvtop', r'op-convert_u'), - Instruction(None, r'\hex{FD}~\hex{00}~\hex{02} \dots'), + Instruction(r'\I8X16.\RELAXEDSWIZZLE', r'\hex{FD}~~\hex{80}~~\hex{02}', r'[\V128~\V128] \to [\V128]', r'valid-relaxed_swizzle', r'exec-relaxed_swizzle', r'op-irelaxed_swizzle'), + Instruction(r'\I32X4.\RELAXEDTRUNC\K{\_f32x4\_s}', r'\hex{FD}~~\hex{81}~~\hex{02}', r'[\V128] \to [\V128]', r'valid-vcvtop', r'exec-vcvtop', r'op-relaxed_trunc_s'), + Instruction(r'\I32X4.\RELAXEDTRUNC\K{\_f32x4\_u}', r'\hex{FD}~~\hex{82}~~\hex{02}', r'[\V128] \to [\V128]', r'valid-vcvtop', r'exec-vcvtop', r'op-relaxed_trunc_u'), + Instruction(r'\I32X4.\RELAXEDTRUNC\K{\_f64x2\_s}', r'\hex{FD}~~\hex{83}~~\hex{02}', r'[\V128] \to [\V128]', r'valid-vcvtop', r'exec-vcvtop', r'op-relaxed_trunc_s'), + Instruction(r'\I32X4.\RELAXEDTRUNC\K{\_f64x2\_u}', r'\hex{FD}~~\hex{84}~~\hex{02}', r'[\V128] \to [\V128]', r'valid-vcvtop', r'exec-vcvtop', r'op-relaxed_trunc_u'), + Instruction(r'\F32X4.\RELAXEDMADD', r'\hex{FD}~~\hex{85}~~\hex{02}', r'[\V128~\V128~\V128] \to [\V128]', r'valid-vternop', r'exec-vternop', r'op-frelaxed_madd'), + Instruction(r'\F32X4.\RELAXEDNMADD', r'\hex{FD}~~\hex{86}~~\hex{02}', r'[\V128~\V128~\V128] \to [\V128]', r'valid-vternop', r'exec-vternop', r'op-frelaxed_nmadd'), + Instruction(r'\F64X2.\RELAXEDMADD', r'\hex{FD}~~\hex{87}~~\hex{02}', r'[\V128~\V128~\V128] \to [\V128]', r'valid-vternop', r'exec-vternop', r'op-frelaxed_madd'), + Instruction(r'\F64X2.\RELAXEDNMADD', r'\hex{FD}~~\hex{88}~~\hex{02}', r'[\V128~\V128~\V128] \to [\V128]', r'valid-vternop', r'exec-vternop', r'op-frelaxed_nmadd'), + Instruction(r'\I8X16.\RELAXEDLANESELECT', r'\hex{FD}~~\hex{89}~~\hex{02}', r'[\V128~\V128~\V128] \to [\V128]', r'valid-relaxed_laneselect', r'exec-relaxed_laneselect', r'op-irelaxed_laneselect'), + Instruction(r'\I16X8.\RELAXEDLANESELECT', r'\hex{FD}~~\hex{8A}~~\hex{02}', r'[\V128~\V128~\V128] \to [\V128]', r'valid-relaxed_laneselect', r'exec-relaxed_laneselect', r'op-irelaxed_laneselect'), + Instruction(r'\I32X4.\RELAXEDLANESELECT', r'\hex{FD}~~\hex{8B}~~\hex{02}', r'[\V128~\V128~\V128] \to [\V128]', r'valid-relaxed_laneselect', r'exec-relaxed_laneselect', r'op-irelaxed_laneselect'), + Instruction(r'\I64X2.\RELAXEDLANESELECT', r'\hex{FD}~~\hex{8C}~~\hex{02}', r'[\V128~\V128~\V128] \to [\V128]', r'valid-relaxed_laneselect', r'exec-relaxed_laneselect', r'op-irelaxed_laneselect'), + Instruction(r'\F32X4.\RELAXEDMIN', r'\hex{FD}~~\hex{8D}~~\hex{02}', r'[\V128~\V128] \to [\V128]', r'valid-vbinop', r'exec-vbinop', r'op-frelaxed_min'), + Instruction(r'\F32X4.\RELAXEDMAX', r'\hex{FD}~~\hex{8E}~~\hex{02}', r'[\V128~\V128] \to [\V128]', r'valid-vbinop', r'exec-vbinop', r'op-frelaxed_max'), + Instruction(r'\F64X2.\RELAXEDMIN', r'\hex{FD}~~\hex{8F}~~\hex{02}', r'[\V128~\V128] \to [\V128]', r'valid-vbinop', r'exec-vbinop', r'op-frelaxed_min'), + Instruction(r'\F64X2.\RELAXEDMAX', r'\hex{FD}~~\hex{90}~~\hex{02}', r'[\V128~\V128] \to [\V128]', r'valid-vbinop', r'exec-vbinop', r'op-frelaxed_max'), + Instruction(r'\I16X8.\RELAXEDQ15MULRS', r'\hex{FD}~~\hex{91}~~\hex{02}', r'[\V128~\V128] \to [\V128]', r'valid-vbinop', r'exec-vbinop', r'op-irelaxed_q15mulr_s'), + Instruction(r'\I16X8.\RELAXEDDOT\K{\_i8x16\_i7x16\_s}', r'\hex{FD}~~\hex{92}~~\hex{02}', r'[\V128~\V128] \to [\V128]', r'valid-relaxed_dot', r'exec-relaxed_dot'), + Instruction(r'\I32X4.\RELAXEDDOT\K{\_i8x16\_i7x16\_add\_s}', r'\hex{FD}~~\hex{93}~~\hex{02}', r'[\V128~\V128~\V128] \to [\V128]', r'valid-relaxed_dot', r'exec-relaxed_dot'), Instruction(None, r'\hex{FE}'), Instruction(None, r'\hex{FF}'), ] diff --git a/document/core/appendix/index.rst b/document/core/appendix/index.rst index 9054e64e3..d3d724709 100644 --- a/document/core/appendix/index.rst +++ b/document/core/appendix/index.rst @@ -7,6 +7,7 @@ Appendix :maxdepth: 2 embedding + profiles implementation properties algorithm diff --git a/document/core/appendix/profiles.rst b/document/core/appendix/profiles.rst new file mode 100644 index 000000000..ea32797d9 --- /dev/null +++ b/document/core/appendix/profiles.rst @@ -0,0 +1,131 @@ +.. index:: ! profile, abstract syntax, validation, execution, binary format, text format +.. _profiles: + +Profiles +-------- + +To enable the use of WebAssembly in as many environments as possible, *profiles* specify coherent language subsets that fit constraints imposed by common classes of host environments. +A host platform can thereby decide to support the language only under a restricted profile, or even the intersection of multiple profiles. + + +Conventions +~~~~~~~~~~~ + +A profile modification is specified by decorating selected rules in the main body of this specification with a *profile annotation* that defines them as conditional on the choice of profile. + +For that purpose, every profile defines a *profile marker*, an alphanumeric short-hand like :math:`\profilename{ABC}`. +A profile annotation of the form :math:`\exprofiles{\profile{ABC}~\profile{XYZ}}` on a rule indicates that this rule is *excluded* for either of the profiles whose marker is :math:`\profilename{ABC}` or :math:`\profilename{XYZ}`. + +There are two ways of subsetting the language in a profile: + +* *Syntactic*, by *omitting* a feature, in which case certain constructs are removed from the syntax altogether. + +* *Semantic*, by *restricting* a feature, in which case certain constructs are still present but some behaviours are ruled out. + + +Syntax Annotations +.................. + +To omit a construct from a profile syntactically, respective productions in the grammar of the :ref:`abstract syntax ` are annotated with an associated profile marker. +This is defined to have the following implications: + +1. Any production in the :ref:`binary ` or :ref:`textual ` syntax that produces abstract syntax with a marked construct is omitted by extension. + +2. Any :ref:`validation ` or :ref:`execution ` rule that handles a marked construct is omitted by extension. + +The overall effect is that the respective construct is no longer part of the language under a respective profile. + +.. note:: + For example, a "busy" profile marked :math:`\profilename{BUSY}` could rule out the |NOP| instruction by marking the production for it in the abstract syntax as follows: + + .. math:: + \begin{array}{llcl} + \production{instruction} & \instr &::=& + \dots \\ + & \exprofiles{\profile{BUSY}} &|& \NOP \\ + & &|& \UNREACHABLE \\ + \end{array} + + A rule may be annotated by multiple markers, which could be the case if a construct is in the intersection of multiple features. + + +Semantics Annotations +..................... + +To restrict certain behaviours in a profile, individual :ref:`validation ` or :ref:`reduction ` rules or auxiliary definitions are annotated with an associated marker. + +This has the consequence that the respective rule is no longer applicable under the given profile. + +.. note:: + For example, an "infinite" profile marked :math:`\profilename{INF}` could define that growing memory never fails: + + .. math:: + \begin{array}{llcl@{\qquad}l} + & S; F; (\I32.\CONST~n)~\MEMORYGROW~x &\stepto& S'; F; (\I32.\CONST~\X{sz}) + \\&&& + \begin{array}[t]{@{}r@{~}l@{}} + (\iff & F.\AMODULE.\MIMEMS[x] = a \\ + \wedge & \X{sz} = |S.\SMEMS[a].\MIDATA|/64\,\F{Ki} \\ + \wedge & S' = S \with \SMEMS[a] = \growmem(S.\SMEMS[a], n)) \\[1ex] + \end{array} + \\[1ex] + \exprofiles{\profile{INF}} & S; F; (\I32.\CONST~n)~\MEMORYGROW~x &\stepto& S; F; (\I32.\CONST~\signed_{32}^{-1}(-1)) + \end{array} + + +Properties +.......... + +All profiles are defined such that the following properties are preserved: + +* All profiles represent syntactic and semantic subsets of the :ref:`full profile `, i.e., they do not *add* syntax or *alter* behaviour. + +* All profiles are mutually compatible, i.e., no two profiles subset semantic behaviour in inconsistent or ambiguous ways, and any intersection of profiles preserves the properties described here. + +* Profiles do not violate :ref:`soundness `, i.e., all :ref:`configurations ` valid under that profile still have well-defined execution behaviour. + +.. note:: + Tools are generally expected to handle and produce code for the full profile by default. + In particular, producers should not generate code that *depends* on specific profiles. Instead, all code should preserve correctness when executed under the full profile. + + Moreover, profiles should be considered static and fixed for a given platform or ecosystem. Runtime conditioning on the "current" profile is not intended and should be avoided. + + + +Defined Profiles +~~~~~~~~~~~~~~~~ + +.. note:: + The number of defined profiles is expected to remain small in the future. Profiles are intended for broad and permanent use cases only. In particular, profiles are not intended for language versioning. + + +.. index:: full profile + single: profile; full +.. _profile-full: + +Full Profile (:math:`{\small{\mathrm{FUL}}}`) +............................................. + +The *full* profile contains the complete language and all possible behaviours. +It imposes no restrictions, i.e., all rules and definitions are active. +All other profiles define sub-languages of this profile. + + +.. index:: determinism, non-determinism, deterministic profile + single: profile; deterministic +.. _profile-deterministic: + +Deterministic Profile (:math:`{\small{\mathrm{DET}}}`) +...................................................... + +The *deterministic* profile excludes all rules marked :math:`\exprofiles{\PROFDET}`. +It defines a sub-language that does not exhibit any incidental non-deterministic behaviour: + +* All :ref:`NaN ` values :ref:`generated ` by :ref:`floating-point instructions ` are canonical and positive. + +* All :ref:`relaxed vector instructions ` have a fixed behaviour that does not depend on the implementation. + +Even under this profile, the |MEMORYGROW| and |TABLEGROW| instructions technically remain :ref:`non-deterministic `, in order to be able to indicate resource exhaustion. + +.. note:: + In future versions of WebAssembly, new non-deterministic behaviour may be added to the language, such that the deterministic profile will induce additional restrictions. diff --git a/document/core/appendix/properties.rst b/document/core/appendix/properties.rst index 4d0da0752..79a530edd 100644 --- a/document/core/appendix/properties.rst +++ b/document/core/appendix/properties.rst @@ -21,7 +21,7 @@ In order to state and prove soundness precisely, the typing rules must be extend .. index:: context, recursive type, recursive type index -.. context-rec: +.. _context-ext: Contexts ~~~~~~~~ @@ -513,10 +513,10 @@ where :math:`\val_1 \gg^+_S \val_2` denotes the transitive closure of the follow .. index:: table type, table instance, limits, function address .. _valid-tableinst: -:ref:`Table Instances ` :math:`\{ \TITYPE~(\limits~t), \TIELEM~\reff^\ast \}` -............................................................................................... +:ref:`Table Instances ` :math:`\{ \TITYPE~\addrtype~\limits~t, \TIELEM~\reff^\ast \}` +....................................................................................................... -* The :ref:`table type ` :math:`\limits~t` must be :ref:`valid ` under the empty :ref:`context `. +* The :ref:`table type ` :math:`\addrtype~\limits~t` must be :ref:`valid ` under the empty :ref:`context `. * The length of :math:`\reff^\ast` must equal :math:`\limits.\LMIN`. @@ -526,11 +526,11 @@ where :math:`\val_1 \gg^+_S \val_2` denotes the transitive closure of the follow * The :ref:`reference type ` :math:`t'_i` must :ref:`match ` the :ref:`reference type ` :math:`t`. -* Then the table instance is valid with :ref:`table type ` :math:`\limits~t`. +* Then the table instance is valid with :ref:`table type ` :math:`\addrtype~\limits~t`. .. math:: \frac{ - \vdashtabletype \limits~t \ok + \vdashtabletype \addrtype~\limits~t \ok \qquad n = \limits.\LMIN \qquad @@ -538,37 +538,37 @@ where :math:`\val_1 \gg^+_S \val_2` denotes the transitive closure of the follow \qquad (\vdashreftypematch t' \matchesvaltype t)^n }{ - S \vdashtableinst \{ \TITYPE~(\limits~t), \TIELEM~\reff^n \} : \limits~t + S \vdashtableinst \{ \TITYPE~\addrtype~\limits~t, \TIELEM~\reff^n \} : \addrtype~\limits~t } .. index:: memory type, memory instance, limits, byte .. _valid-meminst: -:ref:`Memory Instances ` :math:`\{ \MITYPE~\limits, \MIDATA~b^\ast \}` -...................................................................................... +:ref:`Memory Instances ` :math:`\{ \MITYPE~\addrtype~\limits, \MIDATA~b^\ast \}` +................................................................................................ -* The :ref:`memory type ` :math:`\limits` must be :ref:`valid ` under the empty :ref:`context `. +* The :ref:`memory type ` :math:`\addrtype~\limits` must be :ref:`valid ` under the empty :ref:`context `. * The length of :math:`b^\ast` must equal :math:`\limits.\LMIN` multiplied by the :ref:`page size ` :math:`64\,\F{Ki}`. -* Then the memory instance is valid with :ref:`memory type ` :math:`\limits`. +* Then the memory instance is valid with :ref:`memory type ` :math:`\addrtype~\limits`. .. math:: \frac{ - \vdashmemtype \limits \ok + \vdashmemtype \addrtype~\limits \ok \qquad n = \limits.\LMIN \cdot 64\,\F{Ki} }{ - S \vdashmeminst \{ \MITYPE~\limits, \MIDATA~b^n \} : \limits + S \vdashmeminst \{ \MITYPE~\addrtype~\limits, \MIDATA~b^n \} : \addrtype~\limits } .. index:: global type, global instance, value, mutability .. _valid-globalinst: -:ref:`Global Instances ` :math:`\{ \GITYPE~(\mut~t), \GIVALUE~\val \}` -......................................................................................... +:ref:`Global Instances ` :math:`\{ \GITYPE~\mut~t, \GIVALUE~\val \}` +....................................................................................... * The :ref:`global type ` :math:`\mut~t` must be :ref:`valid ` under the empty :ref:`context `. @@ -586,7 +586,7 @@ where :math:`\val_1 \gg^+_S \val_2` denotes the transitive closure of the follow \qquad \vdashvaltypematch t' \matchesvaltype t }{ - S \vdashglobalinst \{ \GITYPE~(\mut~t), \GIVALUE~\val \} : \mut~t + S \vdashglobalinst \{ \GITYPE~\mut~t, \GIVALUE~\val \} : \mut~t } diff --git a/document/core/binary/instructions.rst b/document/core/binary/instructions.rst index c7ded2d17..af31e72eb 100644 --- a/document/core/binary/instructions.rst +++ b/document/core/binary/instructions.rst @@ -290,9 +290,9 @@ Each variant of :ref:`memory instruction ` is encoded with .. math:: \begin{array}{llcllll} \production{memory argument} & \Bmemarg &::=& - a{:}\Bu32~~o{:}\Bu32 &\Rightarrow& 0~\{ \ALIGN~a,~\OFFSET~o \} + a{:}\Bu32~~o{:}\Bu64 &\Rightarrow& 0~\{ \ALIGN~a,~\OFFSET~o \} & (\iff a < 2^6) \\ &&|& - a{:}\Bu32~~x{:}\memidx~~o{:}\Bu32 &\Rightarrow& x~\{ \ALIGN~(a - 2^6),~\OFFSET~o \} + a{:}\Bu32~~x{:}\memidx~~o{:}\Bu64 &\Rightarrow& x~\{ \ALIGN~(a - 2^6),~\OFFSET~o \} & (\iff 2^6 \leq a < 2^7) \\ \production{instruction} & \Binstr &::=& \dots \\ &&|& \hex{28}~~m{:}\Bmemarg &\Rightarrow& \I32.\LOAD~m \\ &&|& @@ -931,6 +931,31 @@ All other vector instructions are plain opcodes without any immediates. \hex{FD}~~95{:}\Bu32 &\Rightarrow& \F64X2.\VPROMOTE\K{\_low\_f32x4}\\ \end{array} +.. math:: + \begin{array}{llclll} + \phantom{\production{instruction}} & \phantom{\Binstr} &\phantom{::=}& \phantom{\dots} && \phantom{vechaslongerinstructionnames} \\[-2ex] &&|& + \hex{FD}~~256{:}\Bu32 &\Rightarrow& \I16X8.\RELAXEDSWIZZLE \\ &&|& + \hex{FD}~~257{:}\Bu32 &\Rightarrow& \I32X4.\RELAXEDTRUNC\K{\_f32x4\_s} \\ &&|& + \hex{FD}~~258{:}\Bu32 &\Rightarrow& \I32X4.\RELAXEDTRUNC\K{\_f32x4\_u} \\ &&|& + \hex{FD}~~259{:}\Bu32 &\Rightarrow& \I32X4.\RELAXEDTRUNC\K{\_f32x4\_s\_zero} \\ &&|& + \hex{FD}~~260{:}\Bu32 &\Rightarrow& \I32X4.\RELAXEDTRUNC\K{\_f32x4\_u\_zero} \\ &&|& + \hex{FD}~~261{:}\Bu32 &\Rightarrow& \F32X4.\RELAXEDMADD \\ &&|& + \hex{FD}~~262{:}\Bu32 &\Rightarrow& \F32X4.\RELAXEDNMADD \\ &&|& + \hex{FD}~~263{:}\Bu32 &\Rightarrow& \F64X2.\RELAXEDMADD \\ &&|& + \hex{FD}~~264{:}\Bu32 &\Rightarrow& \F64X2.\RELAXEDNMADD \\ &&|& + \hex{FD}~~265{:}\Bu32 &\Rightarrow& \I8X16.\RELAXEDLANESELECT \\ &&|& + \hex{FD}~~266{:}\Bu32 &\Rightarrow& \I16X8.\RELAXEDLANESELECT \\ &&|& + \hex{FD}~~267{:}\Bu32 &\Rightarrow& \I32X4.\RELAXEDLANESELECT \\ &&|& + \hex{FD}~~268{:}\Bu32 &\Rightarrow& \I64X2.\RELAXEDLANESELECT \\ &&|& + \hex{FD}~~269{:}\Bu32 &\Rightarrow& \F32X4.\RELAXEDMIN \\ &&|& + \hex{FD}~~270{:}\Bu32 &\Rightarrow& \F32X4.\RELAXEDMAX \\ &&|& + \hex{FD}~~271{:}\Bu32 &\Rightarrow& \F64X2.\RELAXEDMIN \\ &&|& + \hex{FD}~~272{:}\Bu32 &\Rightarrow& \F64X2.\RELAXEDMAX \\ &&|& + \hex{FD}~~273{:}\Bu32 &\Rightarrow& \I16X8.\RELAXEDQ15MULRS \\ &&|& + \hex{FD}~~274{:}\Bu32 &\Rightarrow& \I16X8.\RELAXEDDOT\K{\_i8x16\_i7x16\_s} \\ &&|& + \hex{FD}~~275{:}\Bu32 &\Rightarrow& \I16X8.\RELAXEDDOT\K{\_i8x16\_i7x16\_add\_s} \\ + \end{array} + .. index:: expression pair: binary format; expression diff --git a/document/core/binary/modules.rst b/document/core/binary/modules.rst index bb2b732d4..c0ea6fdfe 100644 --- a/document/core/binary/modules.rst +++ b/document/core/binary/modules.rst @@ -181,7 +181,7 @@ It decodes into a vector of :ref:`imports ` that represent the |M \hex{01}~~\X{tt}{:}\Btabletype &\Rightarrow& \IDTABLE~\X{tt} \\ &&|& \hex{02}~~\X{mt}{:}\Bmemtype &\Rightarrow& \IDMEM~\X{mt} \\ &&|& \hex{03}~~\X{gt}{:}\Bglobaltype &\Rightarrow& \IDGLOBAL~\X{gt} \\ &&|& - \hex{04}~~\X{tt}{:}\Btag &\Rightarrow& \IDTAG~\X{tt} \\ + \hex{04}~~x{:}\Btagtype &\Rightarrow& \IDTAG~x \\ \end{array} @@ -350,7 +350,7 @@ It decodes into a vector of :ref:`element segments ` that represent \production{element segment} & \Belem &::=& 0{:}\Bu32~~e{:}\Bexpr~~y^\ast{:}\Bvec(\Bfuncidx) &\Rightarrow& \\&&&\quad - \{ \ETYPE~(\REF~\NULL~\FUNC), \EINIT~((\REFFUNC~y)~\END)^\ast, \EMODE~\EACTIVE~\{ \ETABLE~0, \EOFFSET~e \} \} \\ &&|& + \{ \ETYPE~(\REF~\FUNC), \EINIT~((\REFFUNC~y)~\END)^\ast, \EMODE~\EACTIVE~\{ \ETABLE~0, \EOFFSET~e \} \} \\ &&|& 1{:}\Bu32~~\X{et}:\Belemkind~~y^\ast{:}\Bvec(\Bfuncidx) &\Rightarrow& \\&&&\quad \{ \ETYPE~\X{et}, \EINIT~((\REFFUNC~y)~\END)^\ast, \EMODE~\EPASSIVE \} \\ &&|& @@ -373,12 +373,12 @@ It decodes into a vector of :ref:`element segments ` that represent &\Rightarrow& \\&&&\quad \{ \ETYPE~et, \EINIT~\X{el}^\ast, \EMODE~\EDECLARATIVE \} \\ \production{element kind} & \Belemkind &::=& - \hex{00} &\Rightarrow& \FUNCREF \\ + \hex{00} &\Rightarrow& (\REF~\FUNC) \\ \end{array} .. note:: The initial integer can be interpreted as a bitfield. - Bit 0 indicates a passive or declarative segment, + Bit 0 distinguishes a passive or declarative segment from an active segment, bit 1 indicates the presence of an explicit table index for an active segment and otherwise distinguishes passive from declarative segments, bit 2 indicates the use of element type and element :ref:`expressions ` instead of element kind and element indices. diff --git a/document/core/binary/types.rst b/document/core/binary/types.rst index 525cc3373..3ab538a09 100644 --- a/document/core/binary/types.rst +++ b/document/core/binary/types.rst @@ -251,13 +251,15 @@ Additional shorthands are recognized for unary recursions and sub types without Limits ~~~~~~ -:ref:`Limits ` are encoded with a preceding flag indicating whether a maximum is present. +:ref:`Limits ` are encoded with a preceding flag indicating whether a maximum is present, and a flag for the :ref:`address type `. .. math:: \begin{array}{llclll} \production{limits} & \Blimits &::=& - \hex{00}~~n{:}\Bu32 &\Rightarrow& \{ \LMIN~n, \LMAX~\epsilon \} \\ &&|& - \hex{01}~~n{:}\Bu32~~m{:}\Bu32 &\Rightarrow& \{ \LMIN~n, \LMAX~m \} \\ + \hex{00}~~n{:}\Bu64 &\Rightarrow& (\I32, \{ \LMIN~n, \LMAX~\epsilon \}) \\ &&|& + \hex{01}~~n{:}\Bu64~~m{:}\Bu64 &\Rightarrow& (\I32, \{ \LMIN~n, \LMAX~m \}) \\ &&|& + \hex{04}~~n{:}\Bu64 &\Rightarrow& (\I64, \{ \LMIN~n, \LMAX~\epsilon \}) \\ &&|& + \hex{05}~~n{:}\Bu64~~m{:}\Bu64 &\Rightarrow& (\I64, \{ \LMIN~n, \LMAX~m \}) \end{array} @@ -273,7 +275,7 @@ Memory Types .. math:: \begin{array}{llclll@{\qquad\qquad}l} \production{memory type} & \Bmemtype &::=& - \X{lim}{:}\Blimits &\Rightarrow& \X{lim} \\ + (\X{at},\X{lim}){:}\Blimits &\Rightarrow& \X{at}~\X{lim} \\ \end{array} @@ -289,7 +291,7 @@ Table Types .. math:: \begin{array}{llclll} \production{table type} & \Btabletype &::=& - \X{et}{:}\Breftype~~\X{lim}{:}\Blimits &\Rightarrow& \X{lim}~\X{et} \\ + \X{et}{:}\Breftype~~(\X{at},\X{lim}){:}\Blimits &\Rightarrow& \X{at}~\X{lim}~\X{et} \\ \end{array} @@ -321,17 +323,14 @@ Global Types Tag Types ~~~~~~~~~ -:ref:`Tag types ` are encoded by their function type. +:ref:`Tag types ` are encoded by a :ref:`type index ` denoting a :ref:`function type `. .. math:: \begin{array}{llclll} \production{tag type} & \Btagtype &::=& - \hex{00}~~ft{:}\Bfunctype &\Rightarrow& ft \\ + \hex{00}~~x{:}\Btypeidx &\Rightarrow& x \\ \end{array} -The |Bfunctype| of a tag is used to characterise exceptions. -The :math:`\hex{00}` bit signifies an exception and is currently the only allowed value. - .. note:: In future versions of WebAssembly, the preceding zero byte may encode additional flags. diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index 497de9826..9019bef4b 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -7,7 +7,7 @@ Instructions WebAssembly computation is performed by executing individual :ref:`instructions `. -.. index:: numeric instruction, determinism, trap, NaN, value, value type +.. index:: numeric instruction, determinism, non-determinism, trap, NaN, value, value type pair: execution; instruction single: abstract syntax; instruction .. _exec-instr-numeric: @@ -803,11 +803,11 @@ Reference Instructions 17. Let :math:`t` be the :ref:`value type ` :math:`\unpacktype(\X{ft})`. -18. For each consecutive subsequence :math:`{b'}^n` of :math:`b^\ast`: +18. For each of the :math:`n` consecutive subsequences :math:`{b'}^z` of :math:`b^\ast`: a. Assert: due to :ref:`validation `, :math:`\bytes_{\X{ft}}` is defined. - b. Let :math:`c_i` be the constant for which :math:`\bytes_{\X{ft}}(c_i)` is :math:`{b'}^n`. + b. Let :math:`c_i` be the constant for which :math:`\bytes_{\X{ft}}(c_i)` is :math:`{b'}^z`. c. Push the value :math:`t.\CONST~c_i` to the stack. @@ -819,14 +819,14 @@ Reference Instructions S; F; (\I32.\CONST~s)~(\I32.\CONST~n)~(\ARRAYNEWDATA~x~y) &\stepto& \TRAP \\&& \begin{array}[t]{@{}r@{~}l@{}} - (\iff & \expanddt(F.\AMODULE.\MITYPES[x]) = \TARRAY~\X{ft}^n \\ + (\iff & \expanddt(F.\AMODULE.\MITYPES[x]) = \TARRAY~\X{ft} \\ \land & s + n\cdot|\X{ft}|/8 > |S.\SDATAS[F.\AMODULE.\MIDATAS[y]].\DIDATA|) \end{array} \\ \\[1ex] S; F; (\I32.\CONST~s)~(\I32.\CONST~n)~(\ARRAYNEWDATA~x~y) &\stepto& (t.\CONST~c)^n~(\ARRAYNEWFIXED~x~n) \\&& \begin{array}[t]{@{}r@{~}l@{}} - (\iff & \expanddt(F.\AMODULE.\MITYPES[x]) = \TARRAY~\X{ft}^n \\ + (\iff & \expanddt(F.\AMODULE.\MITYPES[x]) = \TARRAY~\X{ft} \\ \land & t = \unpacktype(\X{ft}) \\ \land & \concat((\bytes_{\X{ft}}(c))^n) = S.\SDATAS[F.\AMODULE.\MIDATAS[y]].\DIDATA[s \slice n\cdot|\X{ft}|/8] \\ \end{array} \\ @@ -1065,7 +1065,7 @@ Reference Instructions 12. Assert: due to :ref:`validation `, the :ref:`array instance ` :math:`S.\SARRAYS[a]` exists. -13. If :math:`d + n` is larger than or equal to the length of :math:`S.\SARRAYS[a].\AIFIELDS`, then: +13. If :math:`d + n` is larger than the length of :math:`S.\SARRAYS[a].\AIFIELDS`, then: a. Trap. @@ -1102,13 +1102,13 @@ Reference Instructions (\iff d + n > |S.\SARRAYS[a].\AIFIELDS|) \\[1ex] S; (\REFARRAYADDR~a)~(\I32.\CONST~d)~\val~(\I32.\CONST~0)~(\ARRAYFILL~x) - \quad\stepto\quad S; \epsilon + \quad\stepto\quad \epsilon \\ \qquad (\otherwise) \\[1ex] S; (\REFARRAYADDR~a)~(\I32.\CONST~d)~\val~(\I32.\CONST~n+1)~(\ARRAYFILL~x) \quad\stepto - \\ \quad S; + \\ \quad \begin{array}[t]{@{}l@{}} (\REFARRAYADDR~a)~(\I32.\CONST~d)~\val~(\ARRAYSET~x) \\ (\REFARRAYADDR~a)~(\I32.\CONST~d+1)~\val~(\I32.\CONST~n)~(\ARRAYFILL~x) \\ @@ -1175,11 +1175,11 @@ Reference Instructions 23. Assert: due to :ref:`validation `, the :ref:`array instance ` :math:`S.\SARRAYS[a_2]` exists. -24. If :math:`d + n` is larger than or equal to the length of :math:`S.\SARRAYS[a_1].\AIFIELDS`, then: +24. If :math:`d + n` is larger than the length of :math:`S.\SARRAYS[a_1].\AIFIELDS`, then: a. Trap. -25. If :math:`s + n` is larger than or equal to the length of :math:`S.\SARRAYS[a_2].\AIFIELDS`, then: +25. If :math:`s + n` is larger than the length of :math:`S.\SARRAYS[a_2].\AIFIELDS`, then: a. Trap. @@ -1217,13 +1217,13 @@ Reference Instructions a. Push the value :math:`\REFARRAYADDR~a_1` to the stack. - b. Assert: due to the earlier check against the memory size, :math:`d+n-1 < 2^{32}`. + b. Assert: due to the earlier check against the array size, :math:`d+n-1 < 2^{32}`. c. Push the value :math:`\I32.\CONST~(d+n-1)` to the stack. d. Push the value :math:`\REFARRAYADDR~a_2` to the stack. - e. Assert: due to the earlier check against the memory size, :math:`s+n-1 < 2^{32}`. + e. Assert: due to the earlier check against the array size, :math:`s+n-1 < 2^{32}`. f. Push the value :math:`\I32.\CONST~(s+n-1)` to the stack. @@ -1252,7 +1252,7 @@ Reference Instructions (\iff d + n > |S.\SARRAYS[a_1].\AIFIELDS| \vee s + n > |S.\SARRAYS[a_2].\AIFIELDS|) \\[1ex] S; F; (\REFARRAYADDR~a_1)~(\I32.\CONST~d)~(\REFARRAYADDR~a_2)~(\I32.\CONST~s)~(\I32.\CONST~0)~(\ARRAYCOPY~x~y) - \quad\stepto\quad S; \epsilon + \quad\stepto\quad \epsilon \\ \qquad (\otherwise) \\[1ex] @@ -1391,13 +1391,13 @@ Where: \end{array} \\[1ex] S; F; (\REFARRAYADDR~a)~(\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~0)~(\ARRAYINITDATA~x~y) - \quad\stepto\quad S; F; \epsilon + \quad\stepto\quad \epsilon \\ \qquad (\otherwise) \\[1ex] S; F; (\REFARRAYADDR~a)~(\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n+1)~(\ARRAYINITDATA~x~y) \quad\stepto - \\ \quad S; F; + \\ \quad \begin{array}[t]{@{}l@{}} (\REFARRAYADDR~a)~(\I32.\CONST~d)~(t.\CONST~c)~(\ARRAYSET~x) \\ (\REFARRAYADDR~a)~(\I32.\CONST~d+1)~(\I32.\CONST~s+|\X{ft}|/8)~(\I32.\CONST~n)~(\ARRAYINITDATA~x~y) \\ @@ -1497,13 +1497,13 @@ Where: \end{array} \\[1ex] S; F; (\REFARRAYADDR~a)~(\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~0)~(\ARRAYINITELEM~x~y) - \quad\stepto\quad S; F; \epsilon + \quad\stepto\quad \epsilon \\ \qquad (\otherwise) \\[1ex] S; F; (\REFARRAYADDR~a)~(\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n+1)~(\ARRAYINITELEM~x~y) \quad\stepto - \\ \quad S; F; + \\ \quad \begin{array}[t]{@{}l@{}} (\REFARRAYADDR~a)~(\I32.\CONST~d)~\REF~(\ARRAYSET~x) \\ (\REFARRAYADDR~a)~(\I32.\CONST~d+1)~(\I32.\CONST~s+1)~(\I32.\CONST~n)~(\ARRAYINITELEM~x~y) \\ @@ -1538,7 +1538,7 @@ Where: .. math:: \begin{array}{lcl@{\qquad}l} - (\REFNULL \X{ht})~\ANYCONVERTEXTERN &\stepto& (\REFNULL~\ANY) \\ + (\REFNULL~\X{ht})~\ANYCONVERTEXTERN &\stepto& (\REFNULL~\ANY) \\ (\REFEXTERN~\reff)~\ANYCONVERTEXTERN &\stepto& \reff \\ \end{array} @@ -1564,8 +1564,8 @@ Where: .. math:: \begin{array}{lcl@{\qquad}l} - (\REFNULL \X{ht})~\EXTERNCONVERTANY &\stepto& (\REFNULL~\EXTERN) \\ - \reff~\EXTERNCONVERTANY &\stepto& (\REFEXTERN~\reff) & (\iff \reff \neq (\REFNULL \X{ht})) \\ + (\REFNULL~\X{ht})~\EXTERNCONVERTANY &\stepto& (\REFNULL~\EXTERN) \\ + \reff~\EXTERNCONVERTANY &\stepto& (\REFEXTERN~\reff) & (\iff \reff \neq (\REFNULL~\X{ht})) \\ \end{array} @@ -1601,6 +1601,21 @@ Most other vector instructions are defined in terms of numeric operators that ar :math:`\lanes_{\K{i32x4}}(v_1)` and :math:`\lanes_{\K{i32x4}}(v_2)` respectively. +For non-deterministic operators this definition is generalized to sets: + +.. math:: + \begin{array}{lll} + \X{op}_{t\K{x}N}(n_1,\dots,n_k) &=& + \{ \lanes^{-1}_{t\K{x}N}(i^\ast) ~|~ i^\ast \in \Large\times\xref{Step_pure/instructions}{exec-instr-numeric}{\X{op}}_t(i_1,\dots,i_k)^\ast \land i_1^\ast = \lanes_{t\K{x}N}(n_1) \land \dots \land i_k^\ast = \lanes_{t\K{x}N}(n_k) \} \\ + \end{array} + +where :math:`\Large\times \{x^\ast\}^N` transforms a sequence of :math:`N` sets of values into a set of sequences of :math:`N` values by computing the set product: + +.. math:: + \begin{array}{lll} + \Large\times (S_1 \dots S_N) &=& \{ x_1 \dots x_N ~|~ x_1 \in S_1 \land \dots \land x_N \in S_N \} + \end{array} + .. _exec-vconst: @@ -1735,6 +1750,35 @@ Most other vector instructions are defined in terms of numeric operators that ar \end{array} +.. _exec-relaxed_swizzle: + +:math:`\K{i8x16.}\RELAXEDSWIZZLE` +................................. + +.. todo: align the implementation of swizzle and relaxed_swizzle + +1. Assert: due to :ref:`validation `, two values of :ref:`value type ` |V128| are on the top of the stack. + +2. Pop the value :math:`\V128.\VCONST~c_2` from the stack. + +3. Pop the value :math:`\V128.\VCONST~c_1` from the stack. + +4. Let :math:`c'` be the result of computing :math:`\lanes^{-1}_{\I8X16}(\irelaxedswizzle(\lanes_{\I8X16}(c_1), \lanes_{\I8X16}(c_2)))`. + +5. Push the value :math:`\V128.\VCONST~c'` onto the stack. + +.. math:: + \begin{array}{l} + \begin{array}{lcl@{\qquad}l} + (\V128\K{.}\VCONST~c_1)~(\V128\K{.}\VCONST~c_2)~\I8X16\K{.}\irelaxedswizzle &\stepto& (\V128\K{.}\VCONST~c') + \end{array} + \\ \qquad + \begin{array}[t]{@{}r@{~}l@{}} + (\iff & c' = \lanes^{-1}_{\I8X16}(\irelaxedswizzle(\lanes_{\I8X16}(c_1), \lanes_{\I8X16}(c_2))) + \end{array} + \end{array} + + .. _exec-vec-shuffle: :math:`\K{i8x16.}\SHUFFLE~x^\ast` @@ -1776,7 +1820,7 @@ Most other vector instructions are defined in terms of numeric operators that ar :math:`\shape\K{.}\SPLAT` ......................... -1. Let :math:`t` be the type :math:`\unpacked(\shape)`. +1. Let :math:`t` be the type :math:`\unpackshape(\shape)`. 2. Assert: due to :ref:`validation `, a value of :ref:`value type ` :math:`t` is on the top of the stack. @@ -1791,7 +1835,7 @@ Most other vector instructions are defined in terms of numeric operators that ar .. math:: \begin{array}{lcl@{\qquad}l} (t\K{.}\CONST~c_1)~\shape\K{.}\SPLAT &\stepto& (\V128\K{.}\VCONST~c) - & (\iff t = \unpacked(\shape) + & (\iff t = \unpackshape(\shape) \wedge c = \lanes^{-1}_{\shape}(c_1^{\dim(\shape)})) \\ \end{array} @@ -1810,7 +1854,7 @@ Most other vector instructions are defined in terms of numeric operators that ar 4. Let :math:`i^\ast` be the result of computing :math:`\lanes_{t_1\K{x}N}(c_1)`. -5. Let :math:`t_2` be the type :math:`\unpacked(t_1\K{x}N)`. +5. Let :math:`t_2` be the type :math:`\unpackshape(t_1\K{x}N)`. 6. Let :math:`c_2` be the result of computing :math:`\extend^{sx^?}_{t_1,t_2}(i^\ast[x])`. @@ -1823,7 +1867,7 @@ Most other vector instructions are defined in terms of numeric operators that ar \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} - (\iff & t_2 = \unpacked(t_1\K{x}N) \\ + (\iff & t_2 = \unpackshape(t_1\K{x}N) \\ \wedge & c_2 = \extend^{sx^?}_{t_1,t_2}(\lanes_{t_1\K{x}N}(c_1)[x])) \end{array} \end{array} @@ -1836,7 +1880,7 @@ Most other vector instructions are defined in terms of numeric operators that ar 1. Assert: due to :ref:`validation `, :math:`x < \dim(\shape)`. -2. Let :math:`t_2` be the type :math:`\unpacked(\shape)`. +2. Let :math:`t_2` be the type :math:`\unpackshape(\shape)`. 3. Assert: due to :ref:`validation `, a value of :ref:`value type ` :math:`t_1` is on the top of the stack. @@ -1915,6 +1959,66 @@ Most other vector instructions are defined in terms of numeric operators that ar \end{array} +.. _exec-vternop: + +:math:`\shape\K{.}\vternop` +........................... + +1. Assert: due to :ref:`validation `, three values of :ref:`value type ` |V128| are on the top of the stack. + +2. Pop the value :math:`\V128.\VCONST~c_3` from the stack. + +3. Pop the value :math:`\V128.\VCONST~c_2` from the stack. + +4. Pop the value :math:`\V128.\VCONST~c_1` from the stack. + +5. Let :math:`c` be the result of computing :math:`\vternop_{\shape}(c_1, c_2, c_3)`. + +6. Push the value :math:`\V128.\VCONST~c` to the stack. + +.. math:: + \begin{array}{l} + \begin{array}{lcl@{\qquad}l} + (\V128\K{.}\VCONST~c_1)~(\V128\K{.}\VCONST~c_2)~(\V128\K{.}\VCONST~c_3)~\V128\K{.}\vternop &\stepto& (\V128\K{.}\VCONST~c) & + \end{array} + \\ \qquad + \begin{array}[t]{@{}r@{~}l@{}} + (\iff c = \vternop_{\shape}(c_1, c_2, c_3)) \\ + \end{array} + \end{array} + + +.. _exec-relaxed_laneselect: + +:math:`t\K{x}N\K{.}\RELAXEDLANESELECT` +...................................... + +1. Assert: due to :ref:`validation `, three values of :ref:`value type ` |V128| are on the top of the stack. + +2. Pop the value :math:`\V128.\VCONST~c_3` from the stack. + +3. Pop the value :math:`\V128.\VCONST~c_2` from the stack. + +4. Pop the value :math:`\V128.\VCONST~c_1` from the stack. + +5. Let :math:`N` be the :ref:`bit width ` :math:`|t|` of :ref:`value type ` :math:`t`. + +6. Let :math:`c` be the result of computing :math:`\irelaxedlaneselect_{t\K{x}N}(c_1, c_2, c_3)`. + +7. Push the value :math:`\V128.\VCONST~c` to the stack. + +.. math:: + \begin{array}{l} + \begin{array}{lcl@{\qquad}l} + (\V128\K{.}\VCONST~c_1)~(\V128\K{.}\VCONST~c_2)~(\V128\K{.}\VCONST~c_3)~\V128\K{.}\RELAXEDLANESELECT &\stepto& (\V128\K{.}\VCONST~c) & \\ + \end{array} + \\ \qquad + \begin{array}[t]{@{}r@{~}l@{}} + (\iff c = \irelaxedlaneselect_{t\K{x}N}(c_1, c_2, c_3)^\ast \\ + \end{array} + \end{array} + + .. _exec-vrelop: :math:`t\K{x}N\K{.}\vrelop` @@ -2038,7 +2142,7 @@ Most other vector instructions are defined in terms of numeric operators that ar .. math:: \begin{array}{lcl@{\qquad}l} (\V128\K{.}\VCONST~c)~t\K{x}N\K{.}\BITMASK &\stepto& (\I32\K{.}\CONST~i) - & (\iff i = \ibits_{32}^{-1}(\ilts_{|t|}(\lanes_{t\K{x}N}(c), 0^N))) + & (\iff i = \ibits_{32}^{-1}(\ilts_{|t|}(\lanes_{t\K{x}N}(c), (0)^N) (0)^{32-N})) \\ \end{array} @@ -2232,6 +2336,86 @@ where: \end{array} +.. _exec-relaxed_dot: + + +:math:`\K{i16x8.}\RELAXEDDOT\K{\_i8x16\_i7x16\_s}` +.................................................. + +.. todo: move more of this to numerics + +1. Assert: due to :ref:`validation `, two values of :ref:`value type ` |V128| are on the top of the stack. + +2. Pop the value :math:`\V128.\VCONST~c_2` from the stack. + +3. Pop the value :math:`\V128.\VCONST~c_1` from the stack. + +4. Let :math:`(i_1~i_2)^8` be the result of computing :math:`\irelaxeddotmul_{8,16}(\lanes_{\I8X16}(c_1), \lanes_{\I8X16}(c_2))` + +5. Let :math:`j^8` be the result of computing :math:`\iaddsats_{16}(i_1, i_2)^8`. + +6. Let :math:`c` be the result of computing :math:`\lanes^{-1}_{\I16X8}(j^8)`. + +7. Push the value :math:`\V128.\VCONST~c` onto the stack. + +.. math:: + \begin{array}{l} + \begin{array}{llcl@{\qquad}l} + & (\V128\K{.}\VCONST~c_1)~(\V128\K{.}\VCONST~c_2)~\K{i16x8.}\RELAXEDDOT\K{\_i8x16\_i7x16\_s} &\stepto& (\V128\K{.}\VCONST~c) \\ + \end{array} + \\ \qquad + \begin{array}[t]{@{}r@{~}l@{}} + (\iff & (i_1~i_2)^8 = \irelaxeddotmul_{8,16}(\lanes_{\I8X16}(c_1), \lanes_{\I8X16}(c_2)) \\ + \wedge & j^8 = \iaddsats_{16}(i_1, i_2)^8 \\ + \wedge & c = \lanes^{-1}_{\I16X8}(j^8)) + \end{array} + \end{array} + + +:math:`\K{i32x4.}\RELAXEDDOT\K{\_i8x16\_i7x16\_add\_s}` +....................................................... + +.. todo: move more of this to numerics + +1. Assert: due to :ref:`validation `, three values of :ref:`value type ` |V128| are on the top of the stack. + +2. Pop the value :math:`\V128.\VCONST~c_3` from the stack. + +3. Pop the value :math:`\V128.\VCONST~c_2` from the stack. + +4. Pop the value :math:`\V128.\VCONST~c_1` from the stack. + +5. Let :math:`(i_1~i_2)^8` be the result of computing :math:`\irelaxeddotmul_{8,16}(\lanes_{\I8X16}(c_1), \lanes_{\I8X16}(c_2))` + +6. Let :math:`(j_1~j_2)^4` be the result of computing :math:`\iaddsats_{16}(i_1, i_2)^8`. + +7. Let :math:`j^4` be the result of computing :math:`\iadd_{32}(\extends_{16,32}(j_1), \extends_{16,32}(j_2))^4`. + +8. Let :math:`k^4` be the result of computing :math:`\lanes_{\I32X4}(c_3)`. + +9. Let :math:`l^4` be the result of computing :math:`\iadd_{32}(j, k)^4`. + +10. Let :math:`c` be the result of computing :math:`\lanes^{-1}_{\I32X4}(l^4)`. + +11. Push the value :math:`\V128.\VCONST~c` onto the stack. + +.. math:: + \begin{array}{l} + \begin{array}{llcl@{\qquad}l} + & (\V128\K{.}\VCONST~c_1)~(\V128\K{.}\VCONST~c_2)~(\V128\K{.}\VCONST~c_3)~\K{i32x4.}\RELAXEDDOT\K{\_i8x16\_i7x16\_add\_s} &\stepto& (\V128\K{.}\VCONST~c) \\ + \end{array} + \\ \qquad + \begin{array}[t]{@{}r@{~}l@{}} + (\iff & (i_1~i_2)^8 = \irelaxeddotmul_{8,16}(\lanes_{\I8X16}(c_1), \lanes_{\I8X16}(c_2)) \\ + \wedge & (j_1~j_2)^4 = \iaddsats_{16}(i_1, i_2)^8 \\ + \wedge & j^4 = \iadd_{32}(\extends_{16,32}(j_1), \extends_{16,32}(j_2))^4 \\ + \wedge & k^4 = \lanes_{\I32X4}(c_3) \\ + \wedge & l^4 = \iadd_{32}(j, k)^4 \\ + \wedge & c = \lanes^{-1}_{\I32X4}(l^4)) + \end{array} + \end{array} + + .. _exec-vec-extmul: :math:`t_2\K{x}N\K{.}\EXTMUL\K{\_}\half\K{\_}t_1\K{x}M\K{\_}\sx` @@ -2541,9 +2725,9 @@ Table Instructions 5. Let :math:`\X{tab}` be the :ref:`table instance ` :math:`S.\STABLES[a]`. -6. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +6. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -7. Pop the value :math:`\I32.\CONST~i` from the stack. +7. Pop the value :math:`\X{at}.\CONST~i` from the stack. 8. If :math:`i` is not smaller than the length of :math:`\X{tab}.\TIELEM`, then: @@ -2557,12 +2741,12 @@ Table Instructions ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\TABLEGET~x) &\stepto& S; F; \val + S; F; (\X{at}.\CONST~i)~(\TABLEGET~x) &\stepto& S; F; \val \end{array} \\ \qquad (\iff S.\STABLES[F.\AMODULE.\MITABLES[x]].\TIELEM[i] = \val) \\ \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\TABLEGET~x) &\stepto& S; F; \TRAP + S; F; (\X{at}.\CONST~i)~(\TABLEGET~x) &\stepto& S; F; \TRAP \end{array} \\ \qquad (\otherwise) \\ @@ -2588,9 +2772,9 @@ Table Instructions 7. Pop the value :math:`\val` from the stack. -8. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +8. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -9. Pop the value :math:`\I32.\CONST~i` from the stack. +9. Pop the value :math:`\X{at}.\CONST~i` from the stack. 10. If :math:`i` is not smaller than the length of :math:`\X{tab}.\TIELEM`, then: @@ -2602,12 +2786,12 @@ Table Instructions ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~\val~(\TABLESET~x) &\stepto& S'; F; \epsilon + S; F; (\X{at}.\CONST~i)~\val~(\TABLESET~x) &\stepto& S'; F; \epsilon \end{array} \\ \qquad (\iff S' = S \with \STABLES[F.\AMODULE.\MITABLES[x]].\TIELEM[i] = \val) \\ \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~\val~(\TABLESET~x) &\stepto& S; F; \TRAP + S; F; (\X{at}.\CONST~i)~\val~(\TABLESET~x) &\stepto& S; F; \TRAP \end{array} \\ \qquad (\otherwise) \\ @@ -2629,20 +2813,26 @@ Table Instructions 5. Let :math:`\X{tab}` be the :ref:`table instance ` :math:`S.\STABLES[a]`. -6. Let :math:`\X{sz}` be the length of :math:`\X{tab}.\TIELEM`. +6. Let :math:`\X{at}~\limits` be the :ref:`table type ` :math:`\X{tab}.\TITYPE`. + +7. Let :math:`\X{sz}` be the length of :math:`\X{tab}.\TIELEM`. -7. Push the value :math:`\I32.\CONST~\X{sz}` to the stack. +8. Push the value :math:`\X{at}.\CONST~\X{sz}` to the stack. .. math:: \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\TABLESIZE~x) &\stepto& S; F; (\I32.\CONST~\X{sz}) + S; F; (\TABLESIZE~x) &\stepto& S; F; (\X{at}.\CONST~\X{sz}) \end{array} \\ \qquad - (\iff |S.\STABLES[F.\AMODULE.\MITABLES[x]].\TIELEM| = \X{sz}) \\ + \begin{array}[t]{@{}r@{~}l@{}} + (\iff |S.\STABLES[F.\AMODULE.\MITABLES[x]].\TIELEM| = \X{sz} \\ + \wedge S.\STABLES[F.\AMODULE.\MITABLES[x]].\TITYPE = \X{at}~\X{lim}~t) + \end{array} \\ \end{array} +.. index:: determinism, non-determinism .. _exec-table.grow: :math:`\TABLEGROW~x` @@ -2660,35 +2850,35 @@ Table Instructions 6. Let :math:`\X{sz}` be the length of :math:`S.\STABLES[a]`. -7. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +7. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -8. Pop the value :math:`\I32.\CONST~n` from the stack. +8. Pop the value :math:`t.\CONST~n` from the stack. 9. Assert: due to :ref:`validation `, a :ref:`reference value ` is on the top of the stack. 10. Pop the value :math:`\val` from the stack. -11. Let :math:`\X{err}` be the |i32| value :math:`2^{32}-1`, for which :math:`\signed_{32}(\X{err})` is :math:`-1`. +11. Let :math:`\X{err}` be the value :math:`2^{|\X{at}|}-1`, for which :math:`\signed_{|\X{at}|}(\X{err})` is :math:`-1`. 12. Either: a. If :ref:`growing ` :math:`\X{tab}` by :math:`n` entries with initialization value :math:`\val` succeeds, then: - i. Push the value :math:`\I32.\CONST~\X{sz}` to the stack. + i. Push the value :math:`\X{at}.\CONST~\X{sz}` to the stack. b. Else: - i. Push the value :math:`\I32.\CONST~\X{err}` to the stack. + i. Push the value :math:`\X{at}.\CONST~\X{err}` to the stack. 13. Or: - a. push the value :math:`\I32.\CONST~\X{err}` to the stack. + a. push the value :math:`\X{at}.\CONST~\X{err}` to the stack. .. math:: ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; \val~(\I32.\CONST~n)~(\TABLEGROW~x) &\stepto& S'; F; (\I32.\CONST~\X{sz}) + S; F; \val~(\X{at}.\CONST~n)~(\TABLEGROW~x) &\stepto& S'; F; (\X{at}.\CONST~\X{sz}) \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -2698,7 +2888,7 @@ Table Instructions \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} - S; F; \val~(\I32.\CONST~n)~(\TABLEGROW~x) &\stepto& S; F; (\I32.\CONST~\signed_{32}^{-1}(-1)) + S; F; \val~(\X{at}.\CONST~n)~(\TABLEGROW~x) &\stepto& S; F; (\X{at}.\CONST~\signed_{|\X{at}|}^{-1}(-1)) \end{array} \end{array} @@ -2726,60 +2916,60 @@ Table Instructions 5. Let :math:`\X{tab}` be the :ref:`table instance ` :math:`S.\STABLES[\X{ta}]`. -6. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +6. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -7. Pop the value :math:`\I32.\CONST~n` from the stack. +7. Pop the value :math:`\X{at}.\CONST~n` from the stack. 8. Assert: due to :ref:`validation `, a :ref:`reference value ` is on the top of the stack. 9. Pop the value :math:`\val` from the stack. -10. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +10. Assert: due to :ref:`validation `, a value of :ref:`address type ` :math:`\X{at}` is on the top of the stack. -11. Pop the value :math:`\I32.\CONST~i` from the stack. +11. Pop the value :math:`\X{at}.\CONST~i` from the stack. 12. If :math:`i + n` is larger than the length of :math:`\X{tab}.\TIELEM`, then: a. Trap. -12. If :math:`n` is :math:`0`, then: +13. If :math:`n` is :math:`0`, then: a. Return. -13. Push the value :math:`\I32.\CONST~i` to the stack. +14. Push the value :math:`\X{at}.\CONST~i` to the stack. -14. Push the value :math:`\val` to the stack. +15. Push the value :math:`\val` to the stack. -15. Execute the instruction :math:`\TABLESET~x`. +16. Execute the instruction :math:`\TABLESET~x`. -16. Push the value :math:`\I32.\CONST~(i+1)` to the stack. +17. Push the value :math:`\X{at}.\CONST~(i+1)` to the stack. -17. Push the value :math:`\val` to the stack. +18. Push the value :math:`\val` to the stack. -18. Push the value :math:`\I32.\CONST~(n-1)` to the stack. +19. Push the value :math:`\X{at}.\CONST~(n-1)` to the stack. -19. Execute the instruction :math:`\TABLEFILL~x`. +20. Execute the instruction :math:`\TABLEFILL~x`. .. math:: \begin{array}{l} - S; F; (\I32.\CONST~i)~\val~(\I32.\CONST~n)~(\TABLEFILL~x) + S; F; (\X{at}.\CONST~i)~\val~(\X{at}.\CONST~n)~(\TABLEFILL~x) \quad\stepto\quad S; F; \TRAP \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} (\iff & i + n > |S.\STABLES[F.\AMODULE.\MITABLES[x]].\TIELEM|) \\[1ex] \end{array} \\[1ex] - S; F; (\I32.\CONST~i)~\val~(\I32.\CONST~0)~(\TABLEFILL~x) + S; F; (\X{at}.\CONST~i)~\val~(\X{at}.\CONST~0)~(\TABLEFILL~x) \quad\stepto\quad S; F; \epsilon \\ \qquad (\otherwise) \\[1ex] - S; F; (\I32.\CONST~i)~\val~(\I32.\CONST~n+1)~(\TABLEFILL~x) + S; F; (\X{at}.\CONST~i)~\val~(\X{at}.\CONST~n+1)~(\TABLEFILL~x) \quad\stepto \\ \qquad S; F; \begin{array}[t]{@{}l@{}} - (\I32.\CONST~i)~\val~(\TABLESET~x) \\ - (\I32.\CONST~i+1)~\val~(\I32.\CONST~n)~(\TABLEFILL~x) \\ + (\X{at}.\CONST~i)~\val~(\TABLESET~x) \\ + (\X{at}.\CONST~i+1)~\val~(\X{at}.\CONST~n)~(\TABLEFILL~x) \\ \end{array} \\ \qquad (\otherwise) \\ @@ -2809,31 +2999,31 @@ Table Instructions 9. Let :math:`\X{tab}_y` be the :ref:`table instance ` :math:`S.\STABLES[\X{ta}_y]`. -10. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +10. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}_n` is on the top of the stack. -11. Pop the value :math:`\I32.\CONST~n` from the stack. +11. Pop the value :math:`\X{at}_n.\CONST~n` from the stack. -12. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +12. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}_s` is on the top of the stack. -13. Pop the value :math:`\I32.\CONST~s` from the stack. +13. Pop the value :math:`\X{at}_s.\CONST~s` from the stack. -14. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +14. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}_d` is on the top of the stack. -15. Pop the value :math:`\I32.\CONST~d` from the stack. +15. Pop the value :math:`\X{at}_d.\CONST~d` from the stack. 16. If :math:`s + n` is larger than the length of :math:`\X{tab}_y.\TIELEM` or :math:`d + n` is larger than the length of :math:`\X{tab}_x.\TIELEM`, then: a. Trap. -17. If :math:`n = 0`, then: +20. If :math:`n = 0`, then: a. Return. -18. If :math:`d \leq s`, then: +21. If :math:`d \leq s`, then: - a. Push the value :math:`\I32.\CONST~d` to the stack. + a. Push the value :math:`\X{at}_d.\CONST~d` to the stack. - b. Push the value :math:`\I32.\CONST~s` to the stack. + b. Push the value :math:`\X{at}_s.\CONST~s` to the stack. c. Execute the instruction :math:`\TABLEGET~y`. @@ -2841,38 +3031,38 @@ Table Instructions e. Assert: due to the earlier check against the table size, :math:`d+1 < 2^{32}`. - f. Push the value :math:`\I32.\CONST~(d+1)` to the stack. + f. Push the value :math:`\X{at}_d.\CONST~(d+1)` to the stack. g. Assert: due to the earlier check against the table size, :math:`s+1 < 2^{32}`. - h. Push the value :math:`\I32.\CONST~(s+1)` to the stack. + h. Push the value :math:`\X{at}_s.\CONST~(s+1)` to the stack. -19. Else: +22. Else: a. Assert: due to the earlier check against the table size, :math:`d+n-1 < 2^{32}`. - b. Push the value :math:`\I32.\CONST~(d+n-1)` to the stack. + b. Push the value :math:`\X{at}_d.\CONST~(d+n-1)` to the stack. c. Assert: due to the earlier check against the table size, :math:`s+n-1 < 2^{32}`. - d. Push the value :math:`\I32.\CONST~(s+n-1)` to the stack. + d. Push the value :math:`\X{at}_s.\CONST~(s+n-1)` to the stack. c. Execute the instruction :math:`\TABLEGET~y`. f. Execute the instruction :math:`\TABLESET~x`. - g. Push the value :math:`\I32.\CONST~d` to the stack. + g. Push the value :math:`\X{at}_d.\CONST~d` to the stack. - h. Push the value :math:`\I32.\CONST~s` to the stack. + h. Push the value :math:`\X{at}_s.\CONST~s` to the stack. -20. Push the value :math:`\I32.\CONST~(n-1)` to the stack. +23. Push the value :math:`\X{at}_n.\CONST~(n-1)` to the stack. -21. Execute the instruction :math:`\TABLECOPY~x~y`. +24. Execute the instruction :math:`\TABLECOPY~x~y`. .. math:: ~\\[-1ex] \begin{array}{l} - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n)~(\TABLECOPY~x~y) + S; F; (\X{at}_d.\CONST~d)~(\X{at}_s.\CONST~s)~(\X{at}_n.\CONST~n)~(\TABLECOPY~x~y) \quad\stepto\quad S; F; \TRAP \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -2880,27 +3070,27 @@ Table Instructions \vee & d + n > |S.\STABLES[F.\AMODULE.\MITABLES[x]].\TIELEM|) \\[1ex] \end{array} \\[1ex] - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~0)~(\TABLECOPY~x~y) + S; F; (\X{at}_d.\CONST~d)~(\X{at}_s.\CONST~s)~(\X{at}_n.\CONST~0)~(\TABLECOPY~x~y) \quad\stepto\quad S; F; \epsilon \\ \qquad (\otherwise) \\[1ex] - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n+1)~(\TABLECOPY~x~y) + S; F; (\X{at}_d.\CONST~d)~(\X{at}_s.\CONST~s)~(\X{at}_n.\CONST~n+1)~(\TABLECOPY~x~y) \quad\stepto \\ \qquad S; F; \begin{array}[t]{@{}l@{}} - (\I32.\CONST~d)~(\I32.\CONST~s)~(\TABLEGET~y)~(\TABLESET~x) \\ - (\I32.\CONST~d+1)~(\I32.\CONST~s+1)~(\I32.\CONST~n)~(\TABLECOPY~x~y) \\ + (\X{at}_d.\CONST~d)~(\X{at}_s.\CONST~s)~(\TABLEGET~y)~(\TABLESET~x) \\ + (\X{at}_d.\CONST~d+1)~(\X{at}_s.\CONST~s+1)~(\X{at}_n.\CONST~n)~(\TABLECOPY~x~y) \\ \end{array} \\ \qquad (\otherwise, \iff d \leq s) \\[1ex] - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n+1)~(\TABLECOPY~x~y) + S; F; (\X{at}_d.\CONST~d)~(\X{at}_s.\CONST~s)~(\X{at}_n.\CONST~n+1)~(\TABLECOPY~x~y) \quad\stepto \\ \qquad S; F; \begin{array}[t]{@{}l@{}} - (\I32.\CONST~d+n)~(\I32.\CONST~s+n)~(\TABLEGET~y)~(\TABLESET~x) \\ - (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n)~(\TABLECOPY~x~y) \\ + (\X{at}_d.\CONST~d+n)~(\X{at}_s.\CONST~s+n)~(\TABLEGET~y)~(\TABLESET~x) \\ + (\X{at}_d.\CONST~d)~(\X{at}_s.\CONST~s)~(\X{at}_n.\CONST~n)~(\TABLECOPY~x~y) \\ \end{array} \\ \qquad (\otherwise, \iff d > s) \\ @@ -2930,7 +3120,7 @@ Table Instructions 9. Let :math:`\X{elem}` be the :ref:`element instance ` :math:`S.\SELEMS[\X{ea}]`. -10. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +10. Assert: due to :ref:`validation `, a value of :ref:`value type ` :math:`\I32` is on the top of the stack. 11. Pop the value :math:`\I32.\CONST~n` from the stack. @@ -2938,9 +3128,9 @@ Table Instructions 13. Pop the value :math:`\I32.\CONST~s` from the stack. -14. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +14. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -15. Pop the value :math:`\I32.\CONST~d` from the stack. +15. Pop the value :math:`\X{at}.\CONST~d` from the stack. 16. If :math:`s + n` is larger than the length of :math:`\X{elem}.\EIELEM` or :math:`d + n` is larger than the length of :math:`\X{tab}.\TIELEM`, then: @@ -2952,7 +3142,7 @@ Table Instructions 18. Let :math:`\val` be the :ref:`reference value ` :math:`\X{elem}.\EIELEM[s]`. -19. Push the value :math:`\I32.\CONST~d` to the stack. +19. Push the value :math:`\X{at}.\CONST~d` to the stack. 20. Push the value :math:`\val` to the stack. @@ -2960,7 +3150,7 @@ Table Instructions 22. Assert: due to the earlier check against the table size, :math:`d+1 < 2^{32}`. -23. Push the value :math:`\I32.\CONST~(d+1)` to the stack. +23. Push the value :math:`\X{at}.\CONST~(d+1)` to the stack. 24. Assert: due to the earlier check against the segment size, :math:`s+1 < 2^{32}`. @@ -2973,7 +3163,7 @@ Table Instructions .. math:: ~\\[-1ex] \begin{array}{l} - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n)~(\TABLEINIT~x~y) + S; F; (\X{at}.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n)~(\TABLEINIT~x~y) \quad\stepto\quad S; F; \TRAP \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -2981,17 +3171,17 @@ Table Instructions \vee & d + n > |S.\STABLES[F.\AMODULE.\MITABLES[x]].\TIELEM|) \\[1ex] \end{array} \\[1ex] - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~0)~(\TABLEINIT~x~y) + S; F; (\X{at}.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~0)~(\TABLEINIT~x~y) \quad\stepto\quad S; F; \epsilon \\ \qquad (\otherwise) \\[1ex] - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n+1)~(\TABLEINIT~x~y) + S; F; (\X{at}.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n+1)~(\TABLEINIT~x~y) \quad\stepto \\ \qquad S; F; \begin{array}[t]{@{}l@{}} - (\I32.\CONST~d)~\val~(\TABLESET~x) \\ - (\I32.\CONST~d+1)~(\I32.\CONST~s+1)~(\I32.\CONST~n)~(\TABLEINIT~x~y) \\ + (\X{at}.\CONST~d)~\val~(\TABLESET~x) \\ + (\X{at}.\CONST~d+1)~(\I32.\CONST~s+1)~(\I32.\CONST~n)~(\TABLEINIT~x~y) \\ \end{array} \\ \qquad (\otherwise, \iff \val = S.\SELEMS[F.\AMODULE.\MIELEMS[y]].\EIELEM[s]) \\ @@ -3057,9 +3247,9 @@ Memory Instructions 5. Let :math:`\X{mem}` be the :ref:`memory instance ` :math:`S.\SMEMS[a]`. -6. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +6. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -7. Pop the value :math:`\I32.\CONST~i` from the stack. +7. Pop the value :math:`\X{at}.\CONST~i` from the stack. 8. Let :math:`\X{ea}` be the integer :math:`i + \memarg.\OFFSET`. @@ -3089,7 +3279,7 @@ Memory Instructions ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(t.\LOAD~x~\memarg) &\stepto& S; F; (t.\CONST~c) + S; F; (\X{at}.\CONST~i)~(t.\LOAD~x~\memarg) &\stepto& S; F; (t.\CONST~c) \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -3099,7 +3289,7 @@ Memory Instructions \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(t.\LOAD{N}\K{\_}\sx~x~\memarg) &\stepto& + S; F; (\X{at}.\CONST~i)~(t.\LOAD{N}\K{\_}\sx~x~\memarg) &\stepto& S; F; (t.\CONST~\extend^{\sx}_{N,|t|}(n)) \end{array} \\ \qquad @@ -3110,7 +3300,7 @@ Memory Instructions \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(t.\LOAD({N}\K{\_}\sx)^?~x~\memarg) &\stepto& S; F; \TRAP + S; F; (\X{at}.\CONST~i)~(t.\LOAD({N}\K{\_}\sx)^?~x~\memarg) &\stepto& S; F; \TRAP \end{array} \\ \qquad (\otherwise) \\ @@ -3132,9 +3322,9 @@ Memory Instructions 5. Let :math:`\X{mem}` be the :ref:`memory instance ` :math:`S.\SMEMS[a]`. -6. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +6. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -7. Pop the value :math:`\I32.\CONST~i` from the stack. +7. Pop the value :math:`\X{at}.\CONST~i` from the stack. 8. Let :math:`\X{ea}` be the integer :math:`i + \memarg.\OFFSET`. @@ -3158,7 +3348,7 @@ Memory Instructions ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\V128.\LOAD{M}\K{x}N\_\sx~x~\memarg) &\stepto& + S; F; (\X{at}.\CONST~i)~(\V128.\LOAD{M}\K{x}N\_\sx~x~\memarg) &\stepto& S; F; (\V128.\CONST~c) \end{array} \\ \qquad @@ -3171,7 +3361,7 @@ Memory Instructions \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\V128.\LOAD{M}\K{x}N\K{\_}\sx~x~\memarg) &\stepto& S; F; \TRAP + S; F; (\X{at}.\CONST~i)~(\V128.\LOAD{M}\K{x}N\K{\_}\sx~x~\memarg) &\stepto& S; F; \TRAP \end{array} \\ \qquad (\otherwise) \\ @@ -3193,9 +3383,9 @@ Memory Instructions 5. Let :math:`\X{mem}` be the :ref:`memory instance ` :math:`S.\SMEMS[a]`. -6. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +6. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -7. Pop the value :math:`\I32.\CONST~i` from the stack. +7. Pop the value :math:`\X{at}.\CONST~i` from the stack. 8. Let :math:`\X{ea}` be the integer :math:`i + \memarg.\OFFSET`. @@ -3217,7 +3407,7 @@ Memory Instructions ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\V128\K{.}\LOAD{N}\K{\_splat}~x~\memarg) &\stepto& S; F; (\V128.\CONST~c) + S; F; (\X{at}.\CONST~i)~(\V128\K{.}\LOAD{N}\K{\_splat}~x~\memarg) &\stepto& S; F; (\V128.\CONST~c) \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -3228,7 +3418,7 @@ Memory Instructions \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\V128.\LOAD{N}\K{\_splat}~x~\memarg) &\stepto& S; F; \TRAP + S; F; (\X{at}.\CONST~i)~(\V128.\LOAD{N}\K{\_splat}~x~\memarg) &\stepto& S; F; \TRAP \end{array} \\ \qquad (\otherwise) \\ @@ -3250,9 +3440,9 @@ Memory Instructions 5. Let :math:`\X{mem}` be the :ref:`memory instance ` :math:`S.\SMEMS[a]`. -6. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +6. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -7. Pop the value :math:`\I32.\CONST~i` from the stack. +7. Pop the value :math:`\X{at}.\CONST~i` from the stack. 8. Let :math:`\X{ea}` be the integer :math:`i + \memarg.\OFFSET`. @@ -3272,7 +3462,7 @@ Memory Instructions ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\V128\K{.}\LOAD{N}\K{\_zero}~x~\memarg) &\stepto& S; F; (\V128.\CONST~c) + S; F; (\X{at}.\CONST~i)~(\V128\K{.}\LOAD{N}\K{\_zero}~x~\memarg) &\stepto& S; F; (\V128.\CONST~c) \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -3283,7 +3473,7 @@ Memory Instructions \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\V128.\LOAD{N}\K{\_zero}~x~\memarg) &\stepto& S; F; \TRAP + S; F; (\X{at}.\CONST~i)~(\V128.\LOAD{N}\K{\_zero}~x~\memarg) &\stepto& S; F; \TRAP \end{array} \\ \qquad (\otherwise) \\ @@ -3309,9 +3499,9 @@ Memory Instructions 7. Pop the value :math:`\V128.\CONST~v` from the stack. -8. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +8. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -9. Pop the value :math:`\I32.\CONST~i` from the stack. +9. Pop the value :math:`\X{at}.\CONST~i` from the stack. 10. Let :math:`\X{ea}` be the integer :math:`i + \memarg.\OFFSET`. @@ -3335,7 +3525,7 @@ Memory Instructions ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\V128.\CONST~v)~(\V128\K{.}\LOAD{N}\K{\_lane}~x~\memarg~y) &\stepto& S; F; (\V128.\CONST~c) + S; F; (\X{at}.\CONST~i)~(\V128.\CONST~v)~(\V128\K{.}\LOAD{N}\K{\_lane}~x~\memarg~y) &\stepto& S; F; (\V128.\CONST~c) \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -3347,7 +3537,7 @@ Memory Instructions \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\V128.\CONST~v)~(\V128.\LOAD{N}\K{\_lane}~x~\memarg~y) &\stepto& S; F; \TRAP + S; F; (\X{at}.\CONST~i)~(\V128.\CONST~v)~(\V128.\LOAD{N}\K{\_lane}~x~\memarg~y) &\stepto& S; F; \TRAP \end{array} \\ \qquad (\otherwise) \\ @@ -3374,9 +3564,9 @@ Memory Instructions 7. Pop the value :math:`t.\CONST~c` from the stack. -8. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +8. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -9. Pop the value :math:`\I32.\CONST~i` from the stack. +9. Pop the value :math:`\X{at}.\CONST~i` from the stack. 10. Let :math:`\X{ea}` be the integer :math:`i + \memarg.\OFFSET`. @@ -3404,7 +3594,7 @@ Memory Instructions ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(t.\CONST~c)~(t.\STORE~x~\memarg) &\stepto& S'; F; \epsilon + S; F; (\X{at}.\CONST~i)~(t.\CONST~c)~(t.\STORE~x~\memarg) &\stepto& S'; F; \epsilon \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -3414,7 +3604,7 @@ Memory Instructions \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(t.\CONST~c)~(t.\STORE{N}~x~\memarg) &\stepto& S'; F; \epsilon + S; F; (\X{at}.\CONST~i)~(t.\CONST~c)~(t.\STORE{N}~x~\memarg) &\stepto& S'; F; \epsilon \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -3424,7 +3614,7 @@ Memory Instructions \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(t.\CONST~c)~(t.\STORE{N}^?~x~\memarg) &\stepto& S; F; \TRAP + S; F; (\X{at}.\CONST~i)~(t.\CONST~c)~(t.\STORE{N}^?~x~\memarg) &\stepto& S; F; \TRAP \end{array} \\ \qquad (\otherwise) \\ @@ -3450,9 +3640,9 @@ Memory Instructions 7. Pop the value :math:`\V128.\CONST~c` from the stack. -8. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +8. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -9. Pop the value :math:`\I32.\CONST~i` from the stack. +9. Pop the value :math:`\X{at}.\CONST~i` from the stack. 10. Let :math:`\X{ea}` be the integer :math:`i + \memarg.\OFFSET`. @@ -3472,7 +3662,7 @@ Memory Instructions ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\V128.\CONST~c)~(\V128.\STORE{N}\K{\_lane}~x~\memarg~y) &\stepto& S'; F; \epsilon + S; F; (\X{at}.\CONST~i)~(\V128.\CONST~c)~(\V128.\STORE{N}\K{\_lane}~x~\memarg~y) &\stepto& S'; F; \epsilon \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -3483,7 +3673,7 @@ Memory Instructions \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~i)~(\V128.\CONST~c)~(\V128.\STORE{N}\K{\_lane}~x~\memarg~y) &\stepto& S; F; \TRAP + S; F; (\X{at}.\CONST~i)~(\V128.\CONST~c)~(\V128.\STORE{N}\K{\_lane}~x~\memarg~y) &\stepto& S; F; \TRAP \end{array} \\ \qquad (\otherwise) \\ @@ -3505,20 +3695,26 @@ Memory Instructions 5. Let :math:`\X{mem}` be the :ref:`memory instance ` :math:`S.\SMEMS[a]`. -6. Let :math:`\X{sz}` be the length of :math:`\X{mem}.\MIDATA` divided by the :ref:`page size `. +6. Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`\X{mem}.\MITYPE`. + +7. Let :math:`\X{sz}` be the length of :math:`\X{mem}.\MIDATA` divided by the :ref:`page size `. -7. Push the value :math:`\I32.\CONST~\X{sz}` to the stack. +8. Push the value :math:`\X{at}.\CONST~\X{sz}` to the stack. .. math:: \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\MEMORYSIZE~x) &\stepto& S; F; (\I32.\CONST~\X{sz}) + S; F; (\MEMORYSIZE~x) &\stepto& S; F; (\X{at}.\CONST~\X{sz}) \end{array} \\ \qquad - (\iff |S.\SMEMS[F.\AMODULE.\MIMEMS[x]].\MIDATA| = \X{sz}\cdot64\,\F{Ki}) \\ + \begin{array}[t]{@{}r@{~}l@{}} + (\iff |S.\SMEMS[F.\AMODULE.\MIMEMS[x]].\MIDATA| = \X{sz}\cdot64\,\F{Ki} \\ + \wedge S.\SMEMS[F.\AMODULE.\MIMEMS[x]].\MITYPE = \X{at}~\X{lim}) + \end{array} \\ \end{array} +.. index:: determinism, non-determinism .. _exec-memory.grow: :math:`\MEMORYGROW~x` @@ -3536,31 +3732,31 @@ Memory Instructions 6. Let :math:`\X{sz}` be the length of :math:`S.\SMEMS[a]` divided by the :ref:`page size `. -7. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +7. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -8. Pop the value :math:`\I32.\CONST~n` from the stack. +8. Pop the value :math:`\X{at}.\CONST~n` from the stack. -9. Let :math:`\X{err}` be the |i32| value :math:`2^{32}-1`, for which :math:`\signed_{32}(\X{err})` is :math:`-1`. +9. Let :math:`\X{err}` be the :math:`\X{at}` value :math:`2^{|\X{at}|}-1`, for which :math:`\signed_{|\X{at}|}(\X{err})` is :math:`-1`. 10. Either: a. If :ref:`growing ` :math:`\X{mem}` by :math:`n` :ref:`pages ` succeeds, then: - i. Push the value :math:`\I32.\CONST~\X{sz}` to the stack. + i. Push the value :math:`\X{at}.\CONST~\X{sz}` to the stack. b. Else: - i. Push the value :math:`\I32.\CONST~\X{err}` to the stack. + i. Push the value :math:`\X{at}.\CONST~\X{err}` to the stack. 11. Or: - a. Push the value :math:`\I32.\CONST~\X{err}` to the stack. + a. Push the value :math:`\X{at}.\CONST~\X{err}` to the stack. .. math:: ~\\[-1ex] \begin{array}{l} \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~n)~(\MEMORYGROW~x) &\stepto& S'; F; (\I32.\CONST~\X{sz}) + S; F; (\X{at}.\CONST~n)~(\MEMORYGROW~x) &\stepto& S'; F; (\X{at}.\CONST~\X{sz}) \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -3570,7 +3766,7 @@ Memory Instructions \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} - S; F; (\I32.\CONST~n)~(\MEMORYGROW~x) &\stepto& S; F; (\I32.\CONST~\signed_{32}^{-1}(-1)) + S; F; (\X{at}.\CONST~n)~(\MEMORYGROW~x) &\stepto& S; F; (\X{at}.\CONST~\signed_{|\X{at}|}^{-1}(-1)) \end{array} \end{array} @@ -3598,17 +3794,17 @@ Memory Instructions 5. Let :math:`\X{mem}` be the :ref:`memory instance ` :math:`S.\SMEMS[\X{ma}]`. -6. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +6. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -7. Pop the value :math:`\I32.\CONST~n` from the stack. +7. Pop the value :math:`\X{at}.\CONST~n` from the stack. -8. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +8. Assert: due to :ref:`validation `, a value is on the top of the stack. 9. Pop the value :math:`\val` from the stack. -10. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +10. Assert: due to :ref:`validation `, a value of :ref:`address type ` :math:`\X{at}` is on the top of the stack. -11. Pop the value :math:`\I32.\CONST~d` from the stack. +11. Pop the value :math:`\X{at}.\CONST~d` from the stack. 12. If :math:`d + n` is larger than the length of :math:`\X{mem}.\MIDATA`, then: @@ -3622,37 +3818,37 @@ Memory Instructions 15. Push the value :math:`\val` to the stack. -16. Execute the instruction :math:`\I32\K{.}\STORE\K{8}~\{ \OFFSET~0, \ALIGN~0 \}`. +16. Execute the instruction :math:`\X{at}\K{.}\STORE\K{8}~\{ \OFFSET~0, \ALIGN~0 \}`. 17. Assert: due to the earlier check against the memory size, :math:`d+1 < 2^{32}`. -18. Push the value :math:`\I32.\CONST~(d+1)` to the stack. +18. Push the value :math:`\X{at}.\CONST~(d+1)` to the stack. 19. Push the value :math:`\val` to the stack. -20. Push the value :math:`\I32.\CONST~(n-1)` to the stack. +20. Push the value :math:`\X{at}.\CONST~(n-1)` to the stack. 21. Execute the instruction :math:`\MEMORYFILL~x`. .. math:: ~\\[-1ex] \begin{array}{l} - S; F; (\I32.\CONST~d)~\val~(\I32.\CONST~n)~\MEMORYFILL~x + S; F; (\X{at}.\CONST~d)~\val~(\X{at}.\CONST~n)~\MEMORYFILL~x \quad\stepto\quad S; F; \TRAP \\ \qquad (\iff d + n > |S.\SMEMS[F.\AMODULE.\MIMEMS[x]].\MIDATA|) \\[1ex] - S; F; (\I32.\CONST~d)~\val~(\I32.\CONST~0)~\MEMORYFILL~x + S; F; (\X{at}.\CONST~d)~\val~(\X{at}.\CONST~0)~\MEMORYFILL~x \quad\stepto\quad S; F; \epsilon \\ \qquad (\otherwise) \\[1ex] - S; F; (\I32.\CONST~d)~\val~(\I32.\CONST~n+1)~\MEMORYFILL~x + S; F; (\X{at}.\CONST~d)~\val~(\X{at}.\CONST~n+1)~\MEMORYFILL~x \quad\stepto \\ \qquad S; F; \begin{array}[t]{@{}l@{}} - (\I32.\CONST~d)~\val~(\I32\K{.}\STORE\K{8}~x~\{ \OFFSET~0, \ALIGN~0 \}) \\ - (\I32.\CONST~d+1)~\val~(\I32.\CONST~n)~\MEMORYFILL~x \\ + (\X{at}.\CONST~d)~\val~(\X{at}\K{.}\STORE\K{8}~x~\{ \OFFSET~0, \ALIGN~0 \}) \\ + (\X{at}.\CONST~d+1)~\val~(\X{at}.\CONST~n)~\MEMORYFILL~x \\ \end{array} \\ \qquad (\otherwise) \\ @@ -3682,17 +3878,17 @@ Memory Instructions 9. Let :math:`\X{mem}_s` be the :ref:`memory instance ` :math:`S.\SMEMS[\X{sa}]`. -10. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +10. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}_n` is on the top of the stack. -11. Pop the value :math:`\I32.\CONST~n` from the stack. +11. Pop the value :math:`\X{at}_n.\CONST~n` from the stack. -12. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +12. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}_s` is on the top of the stack. -13. Pop the value :math:`\I32.\CONST~s` from the stack. +13. Pop the value :math:`\X{at}_s.\CONST~s` from the stack. -14. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +14. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}_d` is on the top of the stack. -15. Pop the value :math:`\I32.\CONST~d` from the stack. +15. Pop the value :math:`\X{at}_d.\CONST~d` from the stack. 16. If :math:`s + n` is larger than the length of :math:`\X{mem}_s.\MIDATA` or :math:`d + n` is larger than the length of :math:`\X{mem}_d.\MIDATA`, then: @@ -3704,9 +3900,9 @@ Memory Instructions 18. If :math:`d \leq s`, then: - a. Push the value :math:`\I32.\CONST~d` to the stack. + a. Push the value :math:`\X{at}_d.\CONST~d` to the stack. - b. Push the value :math:`\I32.\CONST~s` to the stack. + b. Push the value :math:`\X{at}_s.\CONST~s` to the stack. c. Execute the instruction :math:`\I32\K{.}\LOAD\K{8\_u}~y~\{ \OFFSET~0, \ALIGN~0 \}`. @@ -3714,70 +3910,70 @@ Memory Instructions e. Assert: due to the earlier check against the memory size, :math:`d+1 < 2^{32}`. - f. Push the value :math:`\I32.\CONST~(d+1)` to the stack. + f. Push the value :math:`\X{at}_d.\CONST~(d+1)` to the stack. g. Assert: due to the earlier check against the memory size, :math:`s+1 < 2^{32}`. - h. Push the value :math:`\I32.\CONST~(s+1)` to the stack. + h. Push the value :math:`\X{at}_s.\CONST~(s+1)` to the stack. 19. Else: a. Assert: due to the earlier check against the memory size, :math:`d+n-1 < 2^{32}`. - b. Push the value :math:`\I32.\CONST~(d+n-1)` to the stack. + b. Push the value :math:`\X{at}_d.\CONST~(d+n-1)` to the stack. c. Assert: due to the earlier check against the memory size, :math:`s+n-1 < 2^{32}`. - d. Push the value :math:`\I32.\CONST~(s+n-1)` to the stack. + d. Push the value :math:`\X{at}_s.\CONST~(s+n-1)` to the stack. e. Execute the instruction :math:`\I32\K{.}\LOAD\K{8\_u}~y~\{ \OFFSET~0, \ALIGN~0 \}`. f. Execute the instruction :math:`\I32\K{.}\STORE\K{8}~x~\{ \OFFSET~0, \ALIGN~0 \}`. - g. Push the value :math:`\I32.\CONST~d` to the stack. + g. Push the value :math:`\X{at}_d.\CONST~d` to the stack. - h. Push the value :math:`\I32.\CONST~s` to the stack. + h. Push the value :math:`\X{at}_s.\CONST~s` to the stack. -20. Push the value :math:`\I32.\CONST~(n-1)` to the stack. +20. Push the value :math:`\X{at}_n.\CONST~(n-1)` to the stack. 21. Execute the instruction :math:`\MEMORYCOPY~x~y`. .. math:: ~\\[-1ex] \begin{array}{l} - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n)~\MEMORYCOPY~x~y + S; F; (\X{at}_x.\CONST~d)~(\X{at}_y.\CONST~s)~(\X{at}_n.\CONST~n)~\MEMORYCOPY~x~y \quad\stepto\quad S; F; \TRAP \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} - (\iff & d + n > |S.\SMEMS[F.\AMODULE.\MIMEMS[x]].\MIDATA| \\ - \vee & s + n > |S.\SMEMS[F.\AMODULE.\MIMEMS[y]].\MIDATA|) \\[1ex] + (\iff & (d + n > |S.\SMEMS[F.\AMODULE.\MIMEMS[x]].\MIDATA| \\ + \vee & s + n > |S.\SMEMS[F.\AMODULE.\MIMEMS[y]].\MIDATA|)) \\ \end{array} \\[1ex] - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~0)~\MEMORYCOPY~x~y + S; F; (\X{at}_x.\CONST~d)~(\X{at}_y.\CONST~s)~(\X{at}_n.\CONST~0)~\MEMORYCOPY~x~y \quad\stepto\quad S; F; \epsilon \\ \qquad (\otherwise) \\[1ex] - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n+1)~\MEMORYCOPY~x~y + S; F; (\X{at}_x.\CONST~d)~(\X{at}_y.\CONST~s)~(\X{at}_n.\CONST~n+1)~\MEMORYCOPY~x~y \quad\stepto \\ \qquad S; F; \begin{array}[t]{@{}l@{}} - (\I32.\CONST~d) \\ - (\I32.\CONST~s)~(\I32\K{.}\LOAD\K{8\_u}~y~\{ \OFFSET~0, \ALIGN~0 \}) \\ + (\X{at}_x.\CONST~d) \\ + (\X{at}_y.\CONST~s)~(\I32\K{.}\LOAD\K{8\_u}~y~\{ \OFFSET~0, \ALIGN~0 \}) \\ (\I32\K{.}\STORE\K{8}~x~\{ \OFFSET~0, \ALIGN~0 \}) \\ - (\I32.\CONST~d+1)~(\I32.\CONST~s+1)~(\I32.\CONST~n)~\MEMORYCOPY~x~y \\ + (\X{at}_x.\CONST~d+1)~(\X{at}_y.\CONST~s+1)~(\X{at}_n.\CONST~n)~\MEMORYCOPY~x~y \\ \end{array} \\ \qquad (\otherwise, \iff d \leq s) \\[1ex] - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n+1)~\MEMORYCOPY~x~y + S; F; (\X{at}_x.\CONST~d)~(\X{at}_y.\CONST~s)~(\X{at}_n.\CONST~n+1)~\MEMORYCOPY~x~y \quad\stepto \\ \qquad S; F; \begin{array}[t]{@{}l@{}} - (\I32.\CONST~d+n) \\ - (\I32.\CONST~s+n)~(\I32\K{.}\LOAD\K{8\_u}~y~\{ \OFFSET~0, \ALIGN~0 \}) \\ + (\X{at}_x.\CONST~d+n) \\ + (\X{at}_y.\CONST~s+n)~(\I32\K{.}\LOAD\K{8\_u}~y~\{ \OFFSET~0, \ALIGN~0 \}) \\ (\I32\K{.}\STORE\K{8}~x~\{ \OFFSET~0, \ALIGN~0 \}) \\ - (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n)~\MEMORYCOPY~x~y \\ + (\X{at}_x.\CONST~d)~(\X{at}_y.\CONST~s)~(\X{at}_n.\CONST~n)~\MEMORYCOPY~x~y \\ \end{array} \\ \qquad (\otherwise, \iff d > s) \\ @@ -3815,9 +4011,9 @@ Memory Instructions 13. Pop the value :math:`\I32.\CONST~s` from the stack. -14. Assert: due to :ref:`validation `, a value of :ref:`value type ` |I32| is on the top of the stack. +14. Assert: due to :ref:`validation `, a value of some :ref:`address type ` :math:`\X{at}` is on the top of the stack. -15. Pop the value :math:`\I32.\CONST~d` from the stack. +15. Pop the value :math:`\X{at}.\CONST~d` from the stack. 16. If :math:`s + n` is larger than the length of :math:`\X{data}.\DIDATA` or :math:`d + n` is larger than the length of :math:`\X{mem}.\MIDATA`, then: @@ -3829,15 +4025,15 @@ Memory Instructions 18. Let :math:`b` be the byte :math:`\X{data}.\DIDATA[s]`. -19. Push the value :math:`\I32.\CONST~d` to the stack. +19. Push the value :math:`\X{at}.\CONST~d` to the stack. 20. Push the value :math:`\I32.\CONST~b` to the stack. -21. Execute the instruction :math:`\I32\K{.}\STORE\K{8}~\{ \OFFSET~0, \ALIGN~0 \}`. +21. Execute the instruction :math:`\I32\K{.}\STORE\K{8}~x~\{ \OFFSET~0, \ALIGN~0 \}`. 22. Assert: due to the earlier check against the memory size, :math:`d+1 < 2^{32}`. -23. Push the value :math:`\I32.\CONST~(d+1)` to the stack. +23. Push the value :math:`\X{at}.\CONST~(d+1)` to the stack. 24. Assert: due to the earlier check against the memory size, :math:`s+1 < 2^{32}`. @@ -3850,7 +4046,7 @@ Memory Instructions .. math:: ~\\[-1ex] \begin{array}{l} - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n)~(\MEMORYINIT~x~y) + S; F; (\X{at}.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n)~(\MEMORYINIT~x~y) \quad\stepto\quad S; F; \TRAP \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} @@ -3858,17 +4054,17 @@ Memory Instructions \vee & s + n > |S.\SDATAS[F.\AMODULE.\MIDATAS[y]].\MIDATA|) \\[1ex] \end{array} \\[1ex] - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~0)~(\MEMORYINIT~x~y) + S; F; (\X{at}.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~0)~(\MEMORYINIT~x~y) \quad\stepto\quad S; F; \epsilon \\ \qquad (\otherwise) \\[1ex] - S; F; (\I32.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n+1)~(\MEMORYINIT~x~y) + S; F; (\X{at}.\CONST~d)~(\I32.\CONST~s)~(\I32.\CONST~n+1)~(\MEMORYINIT~x~y) \quad\stepto \\ \qquad S; F; \begin{array}[t]{@{}l@{}} - (\I32.\CONST~d)~(\I32.\CONST~b)~(\I32\K{.}\STORE\K{8}~x~\{ \OFFSET~0, \ALIGN~0 \}) \\ - (\I32.\CONST~d+1)~(\I32.\CONST~s+1)~(\I32.\CONST~n)~(\MEMORYINIT~x~y) \\ + (\X{at}.\CONST~d)~(\I32.\CONST~b)~(\I32\K{.}\STORE\K{8}~x~\{ \OFFSET~0, \ALIGN~0 \}) \\ + (\X{at}.\CONST~d+1)~(\I32.\CONST~s+1)~(\I32.\CONST~n)~(\MEMORYINIT~x~y) \\ \end{array} \\ \qquad (\otherwise, \iff b = S.\SDATAS[F.\AMODULE.\MIDATAS[y]].\DIDATA[s]) \\ @@ -4030,11 +4226,11 @@ Control Instructions 2. Assert: due to :ref:`validation `, :math:`F.\AMODULE.\MITAGS[x]` exists. -3. Let :math:`a` be the :ref:`tag address ` :math:`F.\AMODULE.\MITAGS[x]`. +3. Let :math:`\X{ta}` be the :ref:`tag address ` :math:`F.\AMODULE.\MITAGS[x]`. -4. Assert: due to :ref:`validation `, :math:`S.\STAGS[a]` exists. +4. Assert: due to :ref:`validation `, :math:`S.\STAGS[\X{ta}]` exists. -5. Let :math:`\X{ti}` be the :ref:`tag instance ` :math:`S.\STAGS[a]`. +5. Let :math:`\X{ti}` be the :ref:`tag instance ` :math:`S.\STAGS[\X{ta}]`. 6. Let :math:`[t^n] \toF [{t'}^\ast]` be the :ref:`tag type ` :math:`\X{ti}.\TAGITYPE`. @@ -4042,7 +4238,7 @@ Control Instructions 8. Pop the :math:`n` values :math:`\val^n` from the stack. -9. Let :math:`\X{exn}` be the :ref:`exception instance ` :math:`\{ \EITAG~a, \EIFIELDS~\val^n \}`. +9. Let :math:`\X{exn}` be the :ref:`exception instance ` :math:`\{\EITAG~\X{ta}, \EIFIELDS~\val^n\}`. 10. Let :math:`\X{ea}` be the length of :math:`S.\SEXNS`. @@ -4070,57 +4266,57 @@ Control Instructions :math:`\THROWREF` ................. -1. Let :math:`F` be the :ref:`current ` :ref:`frame `. - -2. Assert: due to :ref:`validation `, a :ref:`reference ` is on the top of the stack. +1. Assert: due to :ref:`validation `, a :ref:`reference ` is on the top of the stack. -3. Pop the reference :math:`\reff` from the stack. +2. Pop the reference :math:`\reff` from the stack. -4. If :math:`\reff` is :math:`\REFNULL~\X{ht}`, then: +3. If :math:`\reff` is :math:`\REFNULL~\X{ht}`, then: a. Trap. -5. Assert: due to :ref:`validation `, :math:`\reff` is an :ref:`exception reference `. +4. Assert: due to :ref:`validation `, :math:`\reff` is an :ref:`exception reference `. -6. Let :math:`\REFEXNADDR~\X{ea}` be :math:`\reff`. +5. Let :math:`\REFEXNADDR~\X{ea}` be :math:`\reff`. -7. Assert: due to :ref:`validation `, :math:`S.\SEXNS[\X{ea}]` exists. +6. Assert: due to :ref:`validation `, :math:`S.\SEXNS[\X{ea}]` exists. -8. Let :math:`\X{exn}` be the :ref:`exception instance ` :math:`S.\SEXNS[\X{ea}]`. +7. Let :math:`\X{exn}` be the :ref:`exception instance ` :math:`S.\SEXNS[\X{ea}]`. -9. Let :math:`a` be the :ref:`tag address ` :math:`\X{exn}.\EITAG`. +8. Let :math:`a` be the :ref:`tag address ` :math:`\X{exn}.\EITAG`. -10. While the stack is not empty and the top of the stack is not an :ref:`exception handler `, do: +9. While the stack is not empty and the top of the stack is not an :ref:`exception handler `, do: a. Pop the top element from the stack. -11. Assert: the stack is now either empty, or there is an exception handler on the top of the stack. +10. Assert: the stack is now either empty, or there is an exception handler on the top of the stack. -12. If the stack is empty, then: +11. If the stack is empty, then: a. Return the exception :math:`(\REFEXNADDR~a)` as a :ref:`result `. -13. Assert: there is an :ref:`exception handler ` on the top of the stack. +12. Assert: there is an :ref:`exception handler ` on the top of the stack. -14. Pop the exception handler :math:`\HANDLER_n\{\catch^\ast\}` from the stack. +13. Pop the exception handler :math:`\HANDLER_n\{\catch^\ast\}` from the stack. -15. If :math:`\catch^\ast` is empty, then: +14. If :math:`\catch^\ast` is empty, then: a. Push the exception reference :math:`\REFEXNADDR~\X{ea}` back to the stack. b. Execute the instruction |THROWREF| again. -16. Else: +15. Else: - a. Let :math:`\catch_1` be the first :ref:`catch clause ` in :math:`\catch^\ast` and :math:`{\catch'}^\ast` the remaining clauses. + a. Let :math:`F` be the :ref:`current ` :ref:`frame `. - b. If :math:`\catch_1` is of the form :math:`\CATCH~x~l` and the :ref:`exception address ` :math:`a` equals :math:`F.\AMODULE.\MITAGS[x]`, then: + b. Let :math:`\catch_1` be the first :ref:`catch clause ` in :math:`\catch^\ast` and :math:`{\catch'}^\ast` the remaining clauses. + + c. If :math:`\catch_1` is of the form :math:`\CATCH~x~l` and the :ref:`tag address ` :math:`a` equals :math:`F.\AMODULE.\MITAGS[x]`, then: i. Push the values :math:`\X{exn}.\EIFIELDS` to the stack. ii. Execute the instruction :math:`\BR~l`. - c. Else if :math:`\catch_1` is of the form :math:`\CATCHREF~x~l` and the :ref:`exception address ` :math:`a` equals :math:`F.\AMODULE.\MITAGS[x]`, then: + d. Else if :math:`\catch_1` is of the form :math:`\CATCHREF~x~l` and the :ref:`tag address ` :math:`a` equals :math:`F.\AMODULE.\MITAGS[x]`, then: i. Push the values :math:`\X{exn}.\EIFIELDS` to the stack. @@ -4128,17 +4324,17 @@ Control Instructions iii. Execute the instruction :math:`\BR~l`. - d. Else if :math:`\catch_1` is of the form :math:`\CATCHALL~l`, then: + e. Else if :math:`\catch_1` is of the form :math:`\CATCHALL~l`, then: i. Execute the instruction :math:`\BR~l`. - e. Else if :math:`\catch_1` is of the form :math:`\CATCHALLREF~l`, then: + f. Else if :math:`\catch_1` is of the form :math:`\CATCHALLREF~l`, then: i. Push the exception reference :math:`\REFEXNADDR~\X{ea}` to the stack. ii. Execute the instruction :math:`\BR~l`. - f. Else: + g. Else: 1. Push the modified handler :math:`\HANDLER_n\{{\catch'}^\ast\}` back to the stack. @@ -4377,7 +4573,7 @@ Control Instructions \begin{array}{lcl@{\qquad}l} S; F; \reff~(\BRONCAST~l~\X{rt}_1~\X{rt}_2) &\stepto& \reff~(\BR~l) & (\iff S \vdashval \reff : \X{rt} - \land \vdashreftypematch \X{rt} \matchesreftype \insttype_{F.\AMODULE}(\X{rt}_2)) \\ + \land {} \vdashreftypematch \X{rt} \matchesreftype \insttype_{F.\AMODULE}(\X{rt}_2)) \\ S; F; \reff~(\BRONCAST~l~\X{rt}_1~\X{rt}_2) &\stepto& \reff & (\otherwise) \\ \end{array} @@ -4412,7 +4608,7 @@ Control Instructions \begin{array}{lcl@{\qquad}l} S; F; \reff~(\BRONCASTFAIL~l~\X{rt}_1~\X{rt}_2) &\stepto& \reff & (\iff S \vdashval \reff : \X{rt} - \land \vdashreftypematch \X{rt} \matchesreftype \insttype_{F.\AMODULE}(\X{rt}_2)) \\ + \land {} \vdashreftypematch \X{rt} \matchesreftype \insttype_{F.\AMODULE}(\X{rt}_2)) \\ S; F; \reff~(\BRONCASTFAIL~l~\X{rt}_1~\X{rt}_2) &\stepto& \reff~(\BR~l) & (\otherwise) \\ \end{array} @@ -4557,7 +4753,7 @@ Control Instructions \begin{array}[t]{@{}r@{~}l@{}} (\iff & S.\STABLES[F.\AMODULE.\MITABLES[x]].\TIELEM[i] = \REFFUNCADDR~a \\ \wedge & S.\SFUNCS[a] = f \\ - \wedge & S \vdashdeftypematch F.\AMODULE.\MITYPES[y] \matchesdeftype f.\FITYPE) + \wedge & S \vdashdeftypematch f.\FITYPE \matchesdeftype F.\AMODULE.\MITYPES[y]) \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} @@ -4739,9 +4935,9 @@ The following auxiliary rules define the semantics of entering and exiting |TRYT Entering :math:`\instr^\ast` with label :math:`L` and exception handler :math:`H` ................................................................................. -1. Push :math:`L` to the stack. +1. Push :math:`H` to the stack. -2. Push :math:`H` onto the stack. +2. Push :math:`L` onto the stack. 3. Jump to the start of the instruction sequence :math:`\instr^\ast`. diff --git a/document/core/exec/modules.rst b/document/core/exec/modules.rst index 47c3f223d..4c667af2f 100644 --- a/document/core/exec/modules.rst +++ b/document/core/exec/modules.rst @@ -94,11 +94,11 @@ are *allocated* in a :ref:`store ` :math:`S`, as defined by the fo 1. Let :math:`\tabletype` be the :ref:`table type ` of the table to allocate and :math:`\reff` the initialization value. -2. Let :math:`(\{\LMIN~n, \LMAX~m^?\}~\reftype)` be the structure of :ref:`table type ` :math:`\tabletype`. +2. Let :math:`(\addrtype~\{\LMIN~n, \LMAX~m^?\}~\reftype)` be the structure of :ref:`table type ` :math:`\tabletype`. 3. Let :math:`a` be the first free :ref:`table address ` in :math:`S`. -4. Let :math:`\tableinst` be the :ref:`table instance ` :math:`\{ \TITYPE~\tabletype', \TIELEM~\reff^n \}` with :math:`n` elements set to :math:`\reff`. +4. Let :math:`\tableinst` be the :ref:`table instance ` :math:`\{ \TITYPE~\tabletype, \TIELEM~\reff^n \}` with :math:`n` elements set to :math:`\reff`. 5. Append :math:`\tableinst` to the |STABLES| of :math:`S`. @@ -108,7 +108,7 @@ are *allocated* in a :ref:`store ` :math:`S`, as defined by the fo \begin{array}{rlll} \alloctable(S, \tabletype, \reff) &=& S', \tableaddr \\[1ex] \mbox{where:} \hfill \\ - \tabletype &=& \{\LMIN~n, \LMAX~m^?\}~\reftype \\ + \tabletype &=& \addrtype~\{\LMIN~n, \LMAX~m^?\}~\reftype \\ \tableaddr &=& |S.\STABLES| \\ \tableinst &=& \{ \TITYPE~\tabletype, \TIELEM~\reff^n \} \\ S' &=& S \compose \{\STABLES~\tableinst\} \\ @@ -123,7 +123,7 @@ are *allocated* in a :ref:`store ` :math:`S`, as defined by the fo 1. Let :math:`\memtype` be the :ref:`memory type ` of the memory to allocate. -2. Let :math:`\{\LMIN~n, \LMAX~m^?\}` be the structure of :ref:`memory type ` :math:`\memtype`. +2. Let :math:`(\addrtype~\{\LMIN~n, \LMAX~m^?\})` be the structure of :ref:`memory type ` :math:`\memtype`. 3. Let :math:`a` be the first free :ref:`memory address ` in :math:`S`. @@ -137,7 +137,7 @@ are *allocated* in a :ref:`store ` :math:`S`, as defined by the fo \begin{array}{rlll} \allocmem(S, \memtype) &=& S', \memaddr \\[1ex] \mbox{where:} \hfill \\ - \memtype &=& \{\LMIN~n, \LMAX~m^?\} \\ + \memtype &=& \addrtype~\{\LMIN~n, \LMAX~m^?\} \\ \memaddr &=& |S.\SMEMS| \\ \meminst &=& \{ \MITYPE~\memtype, \MIDATA~(\hex{00})^{n \cdot 64\,\F{Ki}} \} \\ S' &=& S \compose \{\SMEMS~\meminst\} \\ @@ -258,28 +258,25 @@ Growing :ref:`tables ` 2. Let :math:`\X{len}` be :math:`n` added to the length of :math:`\tableinst.\TIELEM`. -3. If :math:`\X{len}` is larger than or equal to :math:`2^{32}`, then fail. +3. Let :math:`(\addrtype~\limits~\reftype)` be the structure of :ref:`table type ` :math:`\tableinst.\TITYPE`. -4. Let :math:`\limits~t` be the structure of :ref:`table type ` :math:`\tableinst.\TITYPE`. +4. Let :math:`\limits'` be :math:`\limits` with :math:`\LMIN` updated to :math:`\X{len}`. -5. Let :math:`\limits'` be :math:`\limits` with :math:`\LMIN` updated to :math:`\X{len}`. - -6. If :math:`\limits'` is not :ref:`valid `, then fail. +5. If the :ref:`table type ` :math:`(\addrtype~\limits'~\reftype)` is not :ref:`valid `, then fail. -7. Append :math:`\reff^n` to :math:`\tableinst.\TIELEM`. +6. Append :math:`\reff^n` to :math:`\tableinst.\TIELEM`. -8. Set :math:`\tableinst.\TITYPE` to the :ref:`table type ` :math:`\limits'~t`. +7. Set :math:`\tableinst.\TITYPE` to the :ref:`table type ` :math:`(\addrtype~\limits'~\reftype)`. .. math:: \begin{array}{rllll} - \growtable(\tableinst, n, \reff) &=& \tableinst \with \TITYPE = \limits'~t \with \TIELEM = \tableinst.\TIELEM~\reff^n \\ + \growtable(\tableinst, n, \reff) &=& \tableinst \with \TITYPE = \addrtype~\limits'~\reftype \with \TIELEM = \tableinst.\TIELEM~\reff^n \\ && ( \begin{array}[t]{@{}r@{~}l@{}} \iff & \X{len} = n + |\tableinst.\TIELEM| \\ - \wedge & \X{len} < 2^{32} \\ - \wedge & \limits~t = \tableinst.\TITYPE \\ + \wedge & \addrtype~\limits~\reftype = \tableinst.\TITYPE \\ \wedge & \limits' = \limits \with \LMIN = \X{len} \\ - \wedge & \vdashlimits \limits' \ok) \\ + \wedge & \vdashtabletype \addrtype~\limits'~\reftype \ok) \\ \end{array} \\ \end{array} @@ -296,28 +293,25 @@ Growing :ref:`memories ` 3. Let :math:`\X{len}` be :math:`n` added to the length of :math:`\meminst.\MIDATA` divided by the :ref:`page size ` :math:`64\,\F{Ki}`. -4. If :math:`\X{len}` is larger than :math:`2^{16}`, then fail. +4. Let :math:`(\addrtype~\limits)` be the structure of :ref:`memory type ` :math:`\meminst.\MITYPE`. -5. Let :math:`\limits` be the structure of :ref:`memory type ` :math:`\meminst.\MITYPE`. - -6. Let :math:`\limits'` be :math:`\limits` with :math:`\LMIN` updated to :math:`\X{len}`. +5. Let :math:`\limits'` be :math:`\limits` with :math:`\LMIN` updated to :math:`\X{len}`. -7. If :math:`\limits'` is not :ref:`valid `, then fail. +6. If the :ref:`memory type ` :math:`(\addrtype~\limits')` is not :ref:`valid `, then fail. -8. Append :math:`n` times :math:`64\,\F{Ki}` :ref:`bytes ` with value :math:`\hex{00}` to :math:`\meminst.\MIDATA`. +7. Append :math:`n` times :math:`64\,\F{Ki}` :ref:`bytes ` with value :math:`\hex{00}` to :math:`\meminst.\MIDATA`. -9. Set :math:`\meminst.\MITYPE` to the :ref:`memory type ` :math:`\limits'`. +8. Set :math:`\meminst.\MITYPE` to the :ref:`memory type ` :math:`(\addrtype~\limits')`. .. math:: \begin{array}{rllll} - \growmem(\meminst, n) &=& \meminst \with \MITYPE = \limits' \with \MIDATA = \meminst.\MIDATA~(\hex{00})^{n \cdot 64\,\F{Ki}} \\ + \growmem(\meminst, n) &=& \meminst \with \MITYPE = \addrtype~\limits' \with \MIDATA = \meminst.\MIDATA~(\hex{00})^{n \cdot 64\,\F{Ki}} \\ && ( \begin{array}[t]{@{}r@{~}l@{}} \iff & \X{len} = n + |\meminst.\MIDATA| / 64\,\F{Ki} \\ - \wedge & \X{len} \leq 2^{16} \\ - \wedge & \limits = \meminst.\MITYPE \\ + \wedge & \addrtype~\limits = \meminst.\MITYPE \\ \wedge & \limits' = \limits \with \LMIN = \X{len} \\ - \wedge & \vdashlimits \limits' \ok) \\ + \wedge & \vdashmemtype \addrtype~\limits' \ok) \\ \end{array} \\ \end{array} @@ -370,7 +364,7 @@ and list of :ref:`reference ` vectors for the module's :ref:`element 8. For each :ref:`element segment ` :math:`\elem_i` in :math:`\module.\MELEMS`, do: - a. Let :math:`\reftype_i` be the element :ref:`reference type ` obtained by `instantiating ` :math:`\elem_i.\ETYPE` in :math:`\moduleinst` defined below. + a. Let :math:`\reftype_i` be the element :ref:`reference type ` obtained by :ref:`instantiating ` :math:`\elem_i.\ETYPE` in :math:`\moduleinst` defined below. b. Let :math:`\elemaddr_i` be the :ref:`element address ` resulting from :ref:`allocating ` a :ref:`element instance ` of :ref:`reference type ` :math:`\reftype_i` with contents :math:`(\reff_{\F{e}}^\ast)^\ast[i]`. @@ -421,7 +415,7 @@ and list of :ref:`reference ` vectors for the module's :ref:`element 24. Let :math:`\exportinst^\ast` be the concatenation of the :ref:`export instances ` :math:`\exportinst_i` in index order. -25. Let :math:`\moduleinst` be the :ref:`module instance ` :math:`\{\MITYPES~\deftype^\ast,` :math:`\MIFUNCS~\funcaddr_{\F{mod}}^\ast,` :math:`\MITABLES~\tableaddr_{\F{mod}}^\ast,` :math:`\MIMEMS~\memaddr_{\F{mod}}^\ast,` :math:`\MIGLOBALS~\globaladdr_{\F{mod}}^\ast,` :math:`\MITAGS~\tagaddr_{\F{mod}}^\ast`, :math:`\MIEXPORTS~\exportinst^\ast\}`. +25. Let :math:`\moduleinst` be the :ref:`module instance ` :math:`\{\MITYPES~\deftype^\ast,` :math:`\MIFUNCS~\funcaddr_{\F{mod}}^\ast,` :math:`\MITABLES~\tableaddr_{\F{mod}}^\ast,` :math:`\MIMEMS~\memaddr_{\F{mod}}^\ast,` :math:`\MIGLOBALS~\globaladdr_{\F{mod}}^\ast,` :math:`\MITAGS~\tagaddr_{\F{mod}}^\ast`, :math:`\MIELEMS~\elemaddr^\ast,` :math:`\MIDATAS~\dataaddr^\ast,` :math:`\MIEXPORTS~\exportinst^\ast\}`. 26. Return :math:`\moduleinst`. @@ -633,19 +627,17 @@ It is up to the :ref:`embedder ` to define how such conditions are rep 14. For each :ref:`data segment ` :math:`\data_i` in :math:`\module.\MDATAS` whose :ref:`mode ` is of the form :math:`\DACTIVE~\{ \DMEM~\memidx_i, \DOFFSET~\X{dinstr}^\ast_i~\END \}`, do: - a. Assert: :math:`\memidx_i` is :math:`0`. + a. Let :math:`n` be the length of the vector :math:`\data_i.\DINIT`. - b. Let :math:`n` be the length of the vector :math:`\data_i.\DINIT`. + b. :ref:`Execute ` the instruction sequence :math:`\X{dinstr}^\ast_i`. - c. :ref:`Execute ` the instruction sequence :math:`\X{dinstr}^\ast_i`. - - d. :ref:`Execute ` the instruction :math:`\I32.\CONST~0`. + c. :ref:`Execute ` the instruction :math:`\I32.\CONST~0`. - e. :ref:`Execute ` the instruction :math:`\I32.\CONST~n`. + d. :ref:`Execute ` the instruction :math:`\I32.\CONST~n`. - f. :ref:`Execute ` the instruction :math:`\MEMORYINIT~i`. + e. :ref:`Execute ` the instruction :math:`\MEMORYINIT~i`. - g. :ref:`Execute ` the instruction :math:`\DATADROP~i`. + f. :ref:`Execute ` the instruction :math:`\DATADROP~i`. 15. If the :ref:`start function ` :math:`\module.\MSTART` is not empty, then: @@ -695,8 +687,8 @@ where: \F{runelem}_i(\{\ETYPE~\X{et}, \EINIT~\expr^n, \EMODE~\EDECLARATIVE\}) \quad=\\ \qquad (\ELEMDROP~i) \\[1ex] \F{rundata}_i(\{\DINIT~b^n, \DMODE~\DPASSIVE\}) \quad=\\ \qquad \epsilon \\ - \F{rundata}_i(\{\DINIT~b^n, \DMODE~\DACTIVE \{\DMEM~0, \DOFFSET~\instr^\ast~\END\}\}) \quad=\\ \qquad - \instr^\ast~(\I32.\CONST~0)~(\I32.\CONST~n)~(\MEMORYINIT~i)~(\DATADROP~i) \\ + \F{rundata}_i(\{\DINIT~b^n, \DMODE~\DACTIVE \{\DMEM~x, \DOFFSET~\instr^\ast~\END\}\}) \quad=\\ \qquad + \instr^\ast~(\I32.\CONST~0)~(\I32.\CONST~n)~(\MEMORYINIT~x~i)~(\DATADROP~i) \\ \end{array} .. note:: diff --git a/document/core/exec/numerics.rst b/document/core/exec/numerics.rst index 093258e0a..5c1d9be04 100644 --- a/document/core/exec/numerics.rst +++ b/document/core/exec/numerics.rst @@ -1,4 +1,4 @@ -.. index:: value, integer, floating-point, bit width, determinism, NaN +.. index:: value, integer, floating-point, bit width, determinism, non-determinism, NaN .. _exec-op-partial: .. _exec-numeric: @@ -88,8 +88,8 @@ Conventions: .. math:: \begin{array}{lll@{\qquad}l} - \satu_N(i) &=& 2^N-1 & (\iff i > 2^N-1)\\ \satu_N(i) &=& 0 & (\iff i < 0) \\ + \satu_N(i) &=& 2^N-1 & (\iff i > 2^N-1)\\ \satu_N(i) &=& i & (\otherwise) \\ \end{array} @@ -97,8 +97,8 @@ Conventions: .. math:: \begin{array}{lll@{\qquad}l} - \sats_N(i) &=& \signed_N^{-1}(-2^{N-1}) & (\iff i < -2^{N-1})\\ - \sats_N(i) &=& \signed_N^{-1}(2^{N-1}-1) & (\iff i > 2^{N-1}-1)\\ + \sats_N(i) &=& -2^{N-1} & (\iff i < -2^{N-1})\\ + \sats_N(i) &=& 2^{N-1}-1 & (\iff i > 2^{N-1}-1)\\ \sats_N(i) &=& i & (\otherwise) \end{array} @@ -860,11 +860,11 @@ The integer result of predicates -- i.e., :ref:`tests ` and :ref: * Let :math:`j` be the result of adding :math:`j_1` and :math:`j_2`. -* Return :math:`\sats_N(j)`. +* Return the value whose signed interpretation is :math:`\sats_N(j)`. .. math:: \begin{array}{lll@{\qquad}l} - \iaddsats_N(i_1, i_2) &=& \sats_N(\signed_N(i_1) + \signed_N(i_2)) + \iaddsats_N(i_1, i_2) &=& \signed_N^{-1}(\sats_N(\signed_N(i_1) + \signed_N(i_2))) \end{array} @@ -894,11 +894,11 @@ The integer result of predicates -- i.e., :ref:`tests ` and :ref: * Let :math:`j` be the result of subtracting :math:`j_2` from :math:`j_1`. -* Return :math:`\sats_N(j)`. +* Return the value whose signed interpretation is :math:`\sats_N(j)`. .. math:: \begin{array}{lll@{\qquad}l} - \isubsats_N(i_1, i_2) &=& \sats_N(\signed_N(i_1) - \signed_N(i_2)) + \isubsats_N(i_1, i_2) &=& \signed_N^{-1}(\sats_N(\signed_N(i_1) - \signed_N(i_2))) \end{array} @@ -922,11 +922,11 @@ The integer result of predicates -- i.e., :ref:`tests ` and :ref: :math:`\iq15mulrsats_N(i_1, i_2)` ................................. -* Return the result of :math:`\sats_N(\ishrs_N(i_1 \cdot i_2 + 2^{14}, 15))`. +* Return the whose signed interpretation is the result of :math:`\sats_N(\ishrs_N(i_1 \cdot i_2 + 2^{14}, 15))`. .. math:: \begin{array}{lll@{\qquad}l} - \iq15mulrsats_N(i_1, i_2) &=& \sats_N(\ishrs_N(i_1 \cdot i_2 + 2^{14}, 15)) + \iq15mulrsats_N(i_1, i_2) &=& \signed_N^{-1}(\sats_N(\ishrs_N(i_1 \cdot i_2 + 2^{14}, 15))) \end{array} @@ -1033,7 +1033,7 @@ where: \end{array} -.. index:: NaN +.. index:: NaN, determinism, non-determinism .. _aux-nans: NaN Propagation @@ -1046,14 +1046,17 @@ then its sign is non-deterministic and the :ref:`payload ` is co * Otherwise the payload is picked non-deterministically among all :ref:`arithmetic NaNs `; that is, its most significant bit is :math:`1` and all others are unspecified. -This non-deterministic result is expressed by the following auxiliary function producing a set of allowed outputs from a set of inputs: +* In the :ref:`deterministic profile `, however, a positive canonical NaNs is reliably produced in the latter case. + +The non-deterministic result is expressed by the following auxiliary function producing a set of allowed outputs from a set of inputs: .. math:: - \begin{array}{lll@{\qquad}l} - \nans_N\{z^\ast\} &=& \{ + \NAN(n), - \NAN(n) ~|~ n = \canon_N \} - & (\iff \forall \NAN(n) \in z^\ast,~ n = \canon_N) \\ - \nans_N\{z^\ast\} &=& \{ + \NAN(n), - \NAN(n) ~|~ n \geq \canon_N \} - & (\otherwise) \\ + \begin{array}{llcl@{\qquad}l} + & \nans_N\{z^\ast\} &=& \{ + \NAN(\canon_N) \} \\ + \exprofiles{\PROFDET} & \nans_N\{z^\ast\} &=& \{ + \NAN(n), - \NAN(n) ~|~ n = \canon_N \} + & (\iff \{z^\ast\} \subseteq \{ + \NAN(\canon_N), - \NAN(\canon_N) \} \\ + \exprofiles{\PROFDET} & \nans_N\{z^\ast\} &=& \{ + \NAN(n), - \NAN(n) ~|~ n \geq \canon_N \} + & (\iff \{z^\ast\} \not\subseteq \{ + \NAN(\canon_N), - \NAN(\canon_N) \} \\ \end{array} @@ -1236,6 +1239,55 @@ This non-deterministic result is expressed by the following auxiliary function p \end{array} +.. _op-fma: + +:math:`\fma_N(z_1, z_2, z_3)` +............................. + +The function :math:`\fma` is the same as *fusedMultiplyAdd* defined by |IEEE754|_ (Section 5.4.1). +It computes :math:`(z_1 \cdot z_2) + z_3` as if with unbounded range and precision, rounding only once for the final result. + +* If either :math:`z_1` or :math:`z_2` or :math:`z_3` is a NaN, return an element of :math:`\nans_N{z_1, z_2, z_3}`. + +* Else if either :math:`z_1` or :math:`z_2` is a zero and the other is an infinity, then return an element of :math:`\nans_N\{\}`. + +* Else if both :math:`z_1` or :math:`z_2` are infinities of equal sign, and :math:`z_3` is a negative infinity, then return an element of :math:`\nans_N\{\}`. + +* Else if both :math:`z_1` or :math:`z_2` are infinities of opposite sign, and :math:`z_3` is a positive infinity, then return an element of :math:`\nans_N\{\}`. + +* Else if either :math:`z_1` or :math:`z_2` is an infinity and the other is a value of the same sign, and :math:`z_3` is a negative infinity, then return an element of :math:`\nans_N\{\}`. + +* Else if either :math:`z_1` or :math:`z_2` is an infinity and the other is a value of the opposite sign, and :math:`z_3` is a positive infinity, then return an element of :math:`\nans_N\{\}`. + +* Else if both :math:`z_1` and :math:`z_2` are zeroes of the same sign and :math:`z_3` is a zero, then return positive zero. + +* Else if both :math:`z_1` and :math:`z_2` are zeroes of the opposite sign and :math:`z_3` is a positive zero, then return positive zero. + +* Else if both :math:`z_1` and :math:`z_2` are zeroes of the opposite sign and :math:`z_3` is a negative zero, then return negative zero. + +* Else return the result of multiplying :math:`z_1` and :math:`z_2`, adding :math:`z_3` to the intermediate, and the final result ref:`rounded ` to the nearest representable value. + +.. math:: + \begin{array}{@{}llcll} + & \fma_N(\pm \NAN(n), z_2, z_3) &=& \nans_N\{\pm \NAN(n), z_2, z_3\} \\ + & \fma_N(z_1, \pm \NAN(n), z_3) &=& \nans_N\{\pm \NAN(n), z_1, z_3\} \\ + & \fma_N(z_1, z_2, \pm \NAN(n)) &=& \nans_N\{\pm \NAN(n), z_1, z_2\} \\ + & \fma_N(\pm \infty, \pm 0, z_3) &=& \nans_N\{\} \\ + & \fma_N(\pm \infty, \mp 0, z_3) &=& \nans_N\{\} \\ + & \fma_N(\pm \infty, \pm \infty, - \infty) &=& \nans_N\{\} \\ + & \fma_N(\pm \infty, \mp \infty, + \infty) &=& \nans_N\{\} \\ + & \fma_N(\pm q_1, \pm \infty, - \infty) &=& \nans_N\{\} \\ + & \fma_N(\pm q_1, \mp \infty, + \infty) &=& \nans_N\{\} \\ + & \fma_N(\pm \infty, \pm q_1, - \infty) &=& \nans_N\{\} \\ + & \fma_N(\mp \infty, \pm q_1, + \infty) &=& \nans_N\{\} \\ + & \fma_N(\pm 0, \pm 0, \mp 0) &=& + 0 \\ + & \fma_N(\pm 0, \pm 0, \pm 0) &=& + 0 \\ + & \fma_N(\pm 0, \mp 0, + 0) &=& + 0 \\ + & \fma_N(\pm 0, \mp 0, - 0) &=& - 0 \\ + & \fma_N(z_1, z_2, z_3) &=& \ieee_N(z_1 \cdot z_2 + z_3) \\ + \end{array} + + .. _op-fmin: :math:`\fmin_N(z_1, z_2)` @@ -1849,14 +1901,14 @@ Conversions * Else if :math:`z` is positive infinity, then return :math:`2^{N-1} - 1`. -* Else, return :math:`\sats_N(\trunc(z))`. +* Else, return the value whose signed interpretation is :math:`\sats_N(\trunc(z))`. .. math:: \begin{array}{lll@{\qquad}l} \truncsats_{M,N}(\pm \NAN(n)) &=& 0 \\ \truncsats_{M,N}(- \infty) &=& -2^{N-1} \\ \truncsats_{M,N}(+ \infty) &=& 2^{N-1}-1 \\ - \truncsats_{M,N}(z) &=& \sats_N(\trunc(z)) \\ + \truncsats_{M,N}(z) &=& \signed_N^{-1}(\sats_N(\trunc(z))) \\ \end{array} @@ -1954,11 +2006,11 @@ Conversions * Let :math:`j` be the :ref:`signed interpretation ` of :math:`i` of size :math:`M`. -* Return :math:`\sats_N(j)`. +* Return the value whose signed interpretation is :math:`\sats_N(j)`. .. math:: \begin{array}{lll@{\qquad}l} - \narrows_{M,N}(i) &=& \sats_N(\signed_M(i)) + \narrows_{M,N}(i) &=& \signed_N^{-1}(\sats_N(\signed_M(i))) \end{array} @@ -1975,3 +2027,280 @@ Conversions \begin{array}{lll@{\qquad}l} \narrowu_{M,N}(i) &=& \satu_N(\signed_M(i)) \end{array} + + +.. _relaxed-ops: +.. _aux-relaxed: + +Relaxed Operations +~~~~~~~~~~~~~~~~~~ + +The result of *relaxed* operators are *implementation-dependent*, because the set of possible results may depend on properties of the host environment, such as its hardware. +Technically, their behaviour is controlled by a set of *global parameters* to the semantics that an implementation can instantiate in different ways. +These choices are fixed, that is, parameters are constant during the execution of any given program. + +Every such parameter is an index into a sequence of possible sets of results and must be instantiated to a defined index. +In the :ref:`deterministic profile `, every parameter is prescribed to be 0. +This behaviour is expressed by the following auxiliary function, +where :math:`R` is a global parameter selecting one of the allowed outcomes: + +.. math:: + \begin{array}{@{}lcll} + \EXPROFDET & \relaxed(R)[ A_0, \dots, A_n ] = A_R \\ + & \relaxed(R)[ A_0, \dots, A_n ] = A_0 \\ + \end{array} + +.. note:: + Each parameter can be thought of as inducing a family of operations + that is fixed to one particular choice by an implementation. + The fixed operation itself can still be non-deterministic or partial. + + Implementations are expexted to either choose the behaviour that is the most efficient on the underlying hardware, + or the behaviour of the deterministic profile. + + +.. _op-frelaxed_madd: + +:math:`\frelaxedmadd_N(z_1, z_2, z_3)` +...................................... + +The implementation-specific behaviour of this operation is determined by the global parameter :math:`R_{\F{fmadd}} \in \{0, 1\}`. + +* Return :math:`\relaxed(R_{\F{fmadd}})[\fadd_N(\fmul_N(z_1, z_2), z_3)` or :math:`\fma_N(z_1, z_2, z_3)]`. + +.. math:: + \begin{array}{@{}lcll} + \frelaxedmadd_N(z_1, z_2, z_3) &=& \relaxed(R_{\F{fmadd}})[ \fadd_N(\fmul_N(z_1, z_2), z_3), \fma_N(z_1, z_2, z_3) ] \\ + \end{array} + +.. note:: + Relaxed multiply-add allows for fused or unfused results, + which leads to implementation-dependent rounding behaviour. + In the :ref:`deterministic profile `, + the unfused behaviour is used. + + +.. _op-frelaxed_nmadd: + +:math:`\frelaxednmadd_N(z_1, z_2, z_3)` +....................................... + +* Return :math:`\frelaxedmadd(-z_1, z_2, z_3)`. + +.. math:: + \begin{array}{@{}lcll} + \frelaxednmadd_N(z_1, z_2, z_3) &=& \frelaxedmadd_N(-z_1, z_2, z_3) \\ + \end{array} + +.. note:: + This operation is implementation-dependent because :math:`\frelaxedmadd` is implementation-dependent. + + +.. _op-frelaxed_min: + +:math:`\frelaxedmin_N(z_1, z_2)` +................................ + +The implementation-specific behaviour of this operation is determined by the global parameter :math:`R_{\F{fmin}} \in \{0, 1, 2, 3\}`. + +* If :math:`z_1` is a NaN, then return :math:`\relaxed(R_{\F{fmin}})[ \fmin_N(z_1, z_2)`, \NAN(n), z_2, z_2 ]`. + +* If :math:`z_2` is a NaN, then return :math:`\relaxed(R_{\F{fmin}})[ \fmin_N(z_1, z_2)`, z_1, \NAN(n), z_1 ]`. + +* If both :math:`z_1` and :math:`z_2` are zeroes of opposite sign, then return :math:`\relaxed(R_{\F{fmin}})[ \fmin_N(z_1, z_2)`, \pm 0, \mp 0, -0 ]`. + +* Return :math:`\fmin_N(z_1, z_2)`. + +.. math:: + \begin{array}{@{}lcll} + \frelaxedmin_N(\pm \NAN(n), z_2) &=& \relaxed(R_{\F{fmin}})[ \fmin_N(\pm \NAN(n), z_2), \NAN(n), z_2, z_2 ] \\ + \frelaxedmin_N(z_1, \pm \NAN(n)) &=& \relaxed(R_{\F{fmin}})[ \fmin_N(z_1, \pm \NAN(n)), z_1, \NAN(n), z_1 ] \\ + \frelaxedmin_N(\pm 0, \mp 0) &=& \relaxed(R_{\F{fmin}})[ \fmin_N(\pm 0, \mp 0), \pm 0, \mp 0, -0 ] \\ + \frelaxedmin_N(z_1, z_2) &=& \fmin_N(z_1, z_2) & (\otherwise) \\ + \end{array} + +.. note:: + Relaxed minimum is implementation-dependent for NaNs and for zeroes with different signs. + In the :ref:`deterministic profile `, + it behaves like regular :math:`\fmin`. + + +.. _op-frelaxed_max: + +:math:`\frelaxedmax_N(z_1, z_2)` +................................ + +The implementation-specific behaviour of this operation is determined by the global parameter :math:`R_{\F{fmax}} \in \{0, 1, 2, 3\}`. + +* If :math:`z_1` is a NaN, then return :math:`\relaxed(R_{\F{fmax}})[ \fmax_N(z_1, z_2)`, \NAN(n), z_2, z_2 ]`. + +* If :math:`z_2` is a NaN, then return :math:`\relaxed(R_{\F{fmax}})[ \fmax_N(z_1, z_2)`, z_1, \NAN(n), z_1 ]`. + +* If both :math:`z_1` and :math:`z_2` are zeroes of opposite sign, then return :math:`\relaxed(R_{\F{fmax}})[ \fmax_N(z_1, z_2)`, \pm 0, \mp 0, +0 ]`. + +* Return :math:`\fmax_N(z_1, z_2)`. + +.. math:: + \begin{array}{@{}lcll} + \frelaxedmax_N(\pm \NAN(n), z_2) &=& \relaxed(R_{\F{fmax}})[ \fmax_N(\pm \NAN(n), z_2), \NAN(n), z_2, z_2 ] \\ + \frelaxedmax_N(z_1, \pm \NAN(n)) &=& \relaxed(R_{\F{fmax}})[ \fmax_N(z_1, \pm \NAN(n)), z_1, \NAN(n), z_1 ] \\ + \frelaxedmax_N(\pm 0, \mp 0) &=& \relaxed(R_{\F{fmax}})[ \fmax_N(\pm 0, \mp 0), \pm 0, \mp 0, +0 ] \\ + \frelaxedmax_N(z_1, z_2) &=& \fmax_N(z_1, z_2) & (\otherwise) \\ + \end{array} + +.. note:: + Relaxed maximum is implementation-dependent for NaNs and for zeroes with different signs. + In the :ref:`deterministic profile `, + it behaves like regular :math:`\fmax`. + + +.. _op-irelaxed_dot_mul: + +:math:`\irelaxeddotmul_{M,N}(i_1, i_2)` +....................................... + +This is an auxiliary operator for the specification of |RELAXEDDOT|. + +The implementation-specific behaviour of this operation is determined by the global parameter :math:`R_{\F{idot}} \in \{0, 1\}`. + +* Return :math:`\relaxed(R_{\F{idot}})[ \imul_N(\extends_{M,N}(i_1), \extends_{M,N}(i_2)), \imul_N(\extends_{M,N}(i_1), \extendu_{M,N}(i_2)) ]`. + +.. math:: + \begin{array}{@{}lcll} + \irelaxeddotmul_{M,N}(i_1, i_2) &=& \relaxed(R_{\F{idot}})[ \imul_N(\extends_{M,N}(i_1), \extends_{M,N}(i_2)), \imul_N(\extends_{M,N}(i_1), \extendu_{M,N}(i_2)) ] \\ + \end{array} + +.. note:: + Relaxed dot product is implementation-dependent when the second operand is negative in a signed intepretation. + In the :ref:`deterministic profile `, + it behaves like signed dot product. + + +.. _op-irelaxed_q15mulr_s: + +:math:`\irelaxedq15mulrs_N(i_1, i_2)` +..................................... + +The implementation-specific behaviour of this operation is determined by the global parameter :math:`R_{\F{iq15mulr}} \in \{0, 1\}`. + +* If both :math:`i_1` and :math:`i_2` equal :math:`(\signed^{-1}_N(-2^{N-1})`, then return :math:`\relaxed(R_{\F{iq15mulr}})[ 2^{N-1}-1, \signed^{-1}_N(-2^{N-1}) ]`. + +* Return :math:`\iq15mulrsats(i_1, i_2)` + +.. math:: + \begin{array}{@{}lcll} + \irelaxedq15mulrs_N(\signed^{-1}_N(-2^{N-1}), \signed^{-1}_N(-2^{N-1})) &=& \relaxed(R_{\F{iq15mulr}})[ 2^{N-1}-1, \signed^{-1}_N(-2^{N-1}) ] & \\ + \irelaxedq15mulrs_N(i_1, i_2) &=& \iq15mulrsats(i_1, i_2) + \end{array} + +.. note:: + Relaxed Q15 multiplication is implementation-dependent when the result overflows. + In the :ref:`deterministic profile `, + it behaves like regular :math:`\iq15mulrsats`. + + +.. _op-relaxed_trunc: +.. _op-relaxed_trunc_u: + +:math:`\relaxedtrunc^u_{M,N}(z)` +................................ + +The implementation-specific behaviour of this operation is determined by the global parameter :math:`R_{\F{trunc\_u}} \in \{0, 1, 2, 3\}`. + +* If :math:`z` is normal or subnormal and :math:`\trunc(z)` is non-negative and less than :math:`2^N`, then return :math:`\truncu_{M,N}(z)`. + +* Else, return :math:`\relaxed(R_{\F{trunc\_u}})[ \truncsatu_{M,N}(z), 2^N-1, 2^N-2, 2^(N-1) ]`. + +.. math:: + \begin{array}{@{}lcll} + \relaxedtrunc^u_{M,N}(\pm q) &=& \truncu_{M,N}(\pm q) & (\iff 0 \leq \trunc(\pm q) < 2^N) \\ + \relaxedtrunc^u_{M,N}(z) &=& \relaxed(R_{\F{trunc\_u}})[ \truncsatu_{M,N}(z), 2^{N}-1, 2^{N}-2, 2^{N-1}] & (\otherwise) \\ + \end{array} + +.. note:: + Relaxed unsigned truncation is implementation-dependent for NaNs and out-of-range values. + In the :ref:`deterministic profile `, + it behaves like regular :math:`\truncsatu`. + + +.. _op-relaxed_trunc_s: + +:math:`\relaxedtrunc^s_{M,N}(z)` +................................ + +The implementation-specific behaviour of this operation is determined by the global parameter :math:`R_{\F{trunc\_s}} \in \{0, 1\}`. + +* If :math:`z` is normal or subnormal and :math:`\trunc(z)` is greater than or equal to :math:`-2^{N-1}` and less than :math:`2^{N-1}`, then return :math:`\truncs_{M,N}(z)`. + +* Else, return :math:`\relaxed(R_{\F{trunc\_s}})[ \truncsats_{M,N}(z), 2^N-1, 2^N-2, 2^(N-1) ]`. + +.. math:: + \begin{array}{@{}lcll} + \relaxedtrunc^s_{M,N}(\pm q) &=& \truncs_{M,N}(\pm q) & (\iff -2^{N-1} \leq \trunc(\pm q) < 2^{N-1}) \\ + \relaxedtrunc^s_{M,N}(z) &=& \relaxed(R_{\F{trunc\_s}})[ \truncsats_{M,N}(z), \signed^{-1}_N(-2^{N-1})] & (\otherwise) \\ + \end{array} + +.. note:: + Relaxed signed truncation is implementation-dependent for NaNs and out-of-range values. + In the :ref:`deterministic profile `, + it behaves like regular :math:`\truncsats`. + + +.. _op-irelaxed_swizzle: +.. _op-irelaxed_swizzle_lane: + +:math:`\irelaxedswizzle(i^n, j^n)` +.................................. + +The implementation-specific behaviour of this operation is determined by the global parameter :math:`R_{\F{swizzle}} \in \{0, 1\}`. + +* For each :math:`j_k` in :math:`j^n`, let :math:`r_k` be the value :math:`\irelaxedswizzlelane(i^n, j_k)`. + +* Let :math:`r^n` be the concatenation of all :math:`r_k`. + +* Return :math:`r^n`. + +.. math:: + \begin{array}{@{}lcl} + \irelaxedswizzle(i^n, j^n) &=& \irelaxedswizzlelane(i^n, j)^n \\ + \end{array} + +where: + +.. math:: + \begin{array}{@{}lcll} + \irelaxedswizzlelane(i^n, j) &=& i[j] & (\iff j < 16) \\ + \irelaxedswizzlelane(i^n, j) &=& 0 & (\iff \signed_8(j) < 0) \\ + \irelaxedswizzlelane(i^n, j) &=& \relaxed(R_{\F{swizzle}})[ 0, i^n[j \mod n] ] & (\otherwise) \\ + \end{array} + +.. note:: + Relaxed swizzle is implementation-dependent + if the signed interpretation of any of the 8-bit indices in :math:`j^n` is larger than or equal to 16. + In the :ref:`deterministic profile `, + it behaves like regular :math:`\SWIZZLE`. + + +.. _op-irelaxed_laneselect: + +:math:`\irelaxedlaneselect_N(i_1, i_2, i_3)` +............................................ + +The implementation-specific behaviour of this operation is determined by the global parameter :math:`R_{\F{laneselect}} \in \{0, 1\}`. + +* If :math:`i_3` is smaller than :math:`2^{N-1}`, then let :math:`i'_3` be the value :math:`0`, otherwise :math:`2^N-1`. + +* Let :math:`i''_3` be :math:`\relaxed(R_{\F{laneselect}})[i_3, i'_3]`. + +* Return :math:`\ibitselect_N(i_1, i_2, i''_3)`. + +.. math:: + \begin{array}{@{}lcll} + \irelaxedlaneselect_N(i_1, i_2, i_3) &=& \ibitselect_N(i_1, i_2, \relaxed(R_{\F{laneselect}})[ i_3, \extends_{1,N}(\ishru_N(i_3, N-1)) ]) \\ + \end{array} + +.. note:: + Relaxed lane selection is non-deterministic when the mask mixes set and cleared bits, + since the value of the high bit may or may not be expanded to all bits. + In the :ref:`deterministic profile `, + it behaves like :math:`\ibitselect`. diff --git a/document/core/exec/runtime.rst b/document/core/exec/runtime.rst index 6fd25c304..a335ad121 100644 --- a/document/core/exec/runtime.rst +++ b/document/core/exec/runtime.rst @@ -356,18 +356,18 @@ Table Instances ~~~~~~~~~~~~~~~ A *table instance* is the runtime representation of a :ref:`table `. -It records its :ref:`type ` and holds a vector of :ref:`reference values `. +It records its :ref:`type ` and holds a sequence of :ref:`reference values `. .. math:: \begin{array}{llll} \production{table instance} & \tableinst &::=& - \{ \TITYPE~\tabletype, \TIELEM~\vec(\reff) \} \\ + \{ \TITYPE~\tabletype, \TIELEM~\reff^\ast \} \\ \end{array} Table elements can be mutated through :ref:`table instructions `, the execution of an active :ref:`element segment `, or by external means provided by the :ref:`embedder `. It is an invariant of the semantics that all table elements have a type :ref:`matching ` the element type of :math:`\tabletype`. -It also is an invariant that the length of the element vector never exceeds the maximum size of :math:`\tabletype`, if present. +It also is an invariant that the length of the element sequence never exceeds the maximum size of :math:`\tabletype`, if present. .. index:: ! memory instance, memory, byte, ! page size, memory type, embedder, data segment, instruction @@ -380,15 +380,15 @@ Memory Instances ~~~~~~~~~~~~~~~~ A *memory instance* is the runtime representation of a linear :ref:`memory `. -It records its :ref:`type ` and holds a vector of :ref:`bytes `. +It records its :ref:`type ` and holds a sequence of :ref:`bytes `. .. math:: \begin{array}{llll} \production{memory instance} & \meminst &::=& - \{ \MITYPE~\memtype, \MIDATA~\vec(\byte) \} \\ + \{ \MITYPE~\memtype, \MIDATA~\byte^\ast \} \\ \end{array} -The length of the vector always is a multiple of the WebAssembly *page size*, which is defined to be the constant :math:`65536` -- abbreviated :math:`64\,\F{Ki}`. +The length of the sequence always is a multiple of the WebAssembly *page size*, which is defined to be the constant :math:`65536` -- abbreviated :math:`64\,\F{Ki}`. The bytes can be mutated through :ref:`memory instructions `, the execution of an active :ref:`data segment `, or by external means provided by the :ref:`embedder `. @@ -572,7 +572,7 @@ Conventions .. math:: \begin{array}{@{}lcl} \packval_{\valtype}(\val) &=& \val \\ - \packval_{\packedtype}(\I32.\CONST~i) &=& \packedtype.\PACK~(\wrap_{32,|\packtype|}(i)) + \packval_{\packedtype}(\I32.\CONST~i) &=& \packedtype.\PACK~(\wrap_{32,|\packedtype|}(i)) \end{array} * The inverse conversion of a :ref:`field value ` to a regular :ref:`value ` is defined as follows: @@ -726,6 +726,7 @@ Conventions pair:: abstract syntax; administrative instruction .. _syntax-trap: .. _syntax-invoke: +.. _syntax-return_invoke: .. _syntax-instr-admin: Administrative Instructions @@ -896,7 +897,8 @@ Finally, the following definition of *evaluation context* and associated structu \production{evaluation contexts} & E &::=& [\_] ~|~ \val^\ast~E~\instr^\ast ~|~ - \LABEL_n\{\instr^\ast\}~E~\END \\ + \LABEL_n\{\instr^\ast\}~E~\END ~|~ + \HANDLER_n\{\catch^\ast\}~E~\END \\ \end{array} .. math:: diff --git a/document/core/exec/values.rst b/document/core/exec/values.rst index 684e0da4a..5902929a6 100644 --- a/document/core/exec/values.rst +++ b/document/core/exec/values.rst @@ -48,7 +48,7 @@ The following auxiliary typing rules specify this typing relation relative to a * The :ref:`heap type ` must be :ref:`valid ` under the empty :ref:`context `. -* Then value is valid with :ref:`reference type ` :math:`(\REF~\NULL~t')`, where the :ref:`heap type ` :math:`t'` that is the least type that :ref:`matches ` :math:`t`. +* Then the value is valid with :ref:`reference type ` :math:`(\REF~\NULL~t')`, where the :ref:`heap type ` :math:`t'` is the least type that :ref:`matches ` :math:`t`. .. math:: \frac{ diff --git a/document/core/index.bs b/document/core/index.bs index 35fdf277a..f85b5e201 100644 --- a/document/core/index.bs +++ b/document/core/index.bs @@ -7,6 +7,7 @@ Issue Tracking: GitHub https://github.com/WebAssembly/spec/issues Level: 2 TR: https://www.w3.org/TR/wasm-core-2/ ED: https://webassembly.github.io/spec/core/bikeshed/ +Implementation Report: https://webassembly.org/features/ Editor: Andreas Rossberg, w3cid 82328 Repository: WebAssembly/spec Markup Shorthands: css no, markdown no, algorithm no, idl no diff --git a/document/core/index.rst b/document/core/index.rst index 0179df7be..7f39215f2 100644 --- a/document/core/index.rst +++ b/document/core/index.rst @@ -3,7 +3,7 @@ WebAssembly Specification .. only:: html - | Release |release| + | Release |release| (Draft, |today|) | Editor: Andreas Rossberg diff --git a/document/core/static/custom.css b/document/core/static/custom.css index 33bb863d4..950236af0 100644 --- a/document/core/static/custom.css +++ b/document/core/static/custom.css @@ -15,10 +15,16 @@ body { font-size: 15px; } -div.document { width: 1000px; } +div.document { + width: unset; + max-width: 1000px; +} div.bodywrapper { margin: 0 0 0 200px; } div.body { padding: 0 10px 0 10px; } -div.footer { width: 1000px; } +div.footer { + width: unset; + max-width: 1000px; +} div.body h1 { font-size: 200%; } div.body h2 { font-size: 150%; } diff --git a/document/core/syntax/instructions.rst b/document/core/syntax/instructions.rst index bc718b4d0..615c1c6b9 100644 --- a/document/core/syntax/instructions.rst +++ b/document/core/syntax/instructions.rst @@ -190,7 +190,10 @@ Occasionally, it is convenient to group operators together according to the foll .. _syntax-visatbinop: .. _syntax-vfunop: .. _syntax-vfbinop: +.. _syntax-rvfbinop: +.. _syntax-rvfternop: .. _syntax-instr-vec: +.. _syntax-instr-relaxed: Vector Instructions ~~~~~~~~~~~~~~~~~~~ @@ -273,8 +276,16 @@ Vector instructions (also known as *SIMD* instructions, *single instruction mult \K{f32x4.}\VCONVERT\K{\_i32x4\_}\sx ~|~ \K{f32x4.}\VDEMOTE\K{\_f64x2\_zero} \\&&|& \K{f64x2.}\VCONVERT\K{\_low\_i32x4\_}\sx ~|~ - \K{f64x2.}\VPROMOTE\K{\_low\_f32x4} \\&&|& - \dots \\ + \K{f64x2.}\VPROMOTE\K{\_low\_f32x4} \\ + & &|& \K{i8x16.}\RELAXEDSWIZZLE \\ + & &|& \K{i16x8.}\RELAXEDQ15MULRS \\ + & &|& \K{i32x4.}\RELAXEDTRUNC\K{\_f32x4\_}\sx \\ + & &|& \K{i16x8.}\RELAXEDDOT\K{\_i8x16\_i7x16\_s} \\ + & &|& \K{i32x4.}\RELAXEDDOT\K{\_i8x16\_i7x16\_add\_s} \\ + & &|& \ishape\K{.}\RELAXEDLANESELECT \\ + & &|& \fshape\K{.}\rvfternop \\ + & &|& \fshape\K{.}\rvfbinop \\ + & &|& \dots \\ \end{array} .. math:: @@ -338,6 +349,12 @@ Vector instructions (also known as *SIMD* instructions, *single instruction mult \K{max} ~|~ \K{pmin} ~|~ \K{pmax} \\ + \production{relaxed vector floating-point binary operator} & \rvfbinop &::=& + \K{relaxed\_min} ~|~ + \K{relaxed\_max} \\ + \production{relaxed vector floating-point ternary operator} & \rvfternop &::=& + \K{relaxed\_madd} ~|~ + \K{relaxed\_nmadd} \\ \end{array} .. _syntax-vec-shape: @@ -382,6 +399,7 @@ For the other vector instructions, the use of two's complement for the signed in .. _syntax-vunop: .. _syntax-vbinop: .. _syntax-vrelop: +.. _syntax-vternop: .. _syntax-vtestop: .. _syntax-vcvtop: @@ -399,9 +417,14 @@ Occasionally, it is convenient to group operators together according to the foll \production{binary operator} & \vbinop &::=& \vibinop ~|~ \vfbinop \\&&|& \viminmaxop ~|~ \visatbinop \\&&|& + \rvfbinop \\&&|& \VMUL ~|~ \AVGR\K{\_u} ~|~ - \Q15MULRSAT\K{\_s} \\ + \Q15MULRSAT\K{\_s} \\&&|& + \RELAXEDQ15MULRS\K{\_s} \\ + \production{ternary operator} & \vternop &::=& + \vvternop ~|~ + \rvfternop \\ \production{test operator} & \vtestop &::=& \vitestop \\ \production{relational operator} & \vrelop &::=& @@ -411,7 +434,8 @@ Occasionally, it is convenient to group operators together according to the foll \VTRUNC\K{\_sat} ~|~ \VCONVERT ~|~ \VDEMOTE ~|~ - \VPROMOTE \\ + \VPROMOTE ~|~ + \RELAXEDTRUNC \\ \end{array} @@ -650,7 +674,7 @@ Instructions in this group are concerned with linear :ref:`memory `. .. math:: \begin{array}{llrl} \production{memory immediate} & \memarg &::=& - \{ \OFFSET~\u32, \ALIGN~\u32 \} \\ + \{ \OFFSET~\u64, \ALIGN~\u32 \} \\ \production{lane width} & \X{ww} &::=& 8 ~|~ 16 ~|~ 32 ~|~ 64 \\ \production{instruction} & \instr &::=& @@ -683,7 +707,7 @@ Instructions in this group are concerned with linear :ref:`memory `. \DATADROP~\dataidx \\ \end{array} -Memory is accessed with |LOAD| and |STORE| instructions for the different :ref:`number types ` and `vector types `. +Memory is accessed with |LOAD| and |STORE| instructions for the different :ref:`number types ` and :ref:`vector types `. They all take a :ref:`memory index ` and a *memory immediate* |memarg| that contains an address *offset* and the expected *alignment* (expressed as the exponent of a power of 2). Integer loads and stores can optionally specify a *storage size* that is smaller than the :ref:`bit width ` of the respective value type. @@ -692,13 +716,10 @@ In the case of loads, a sign extension mode |sx| is then required to select appr Vector loads can specify a shape that is half the :ref:`bit width ` of |V128|. Each lane is half its usual size, and the sign extension mode |sx| then specifies how the smaller lane is extended to the larger lane. Alternatively, vector loads can perform a *splat*, such that only a single lane of the specified storage size is loaded, and the result is duplicated to all lanes. -The static address offset is added to the dynamic address operand, yielding a 33 bit *effective address* that is the zero-based index at which the memory is accessed. +The static address offset is added to the dynamic address operand, yielding a 33-bit or 65-bit *effective address* that is the zero-based index at which the memory is accessed. All values are read and written in |LittleEndian|_ byte order. A :ref:`trap ` results if any of the accessed memory bytes lies outside the address range implied by the memory's current size. -.. note:: - Future versions of WebAssembly might provide memory instructions with 64 bit address ranges. - The |MEMORYSIZE| instruction returns the current size of a memory. The |MEMORYGROW| instruction grows a memory by a given delta and returns the previous size, or :math:`-1` if enough memory cannot be allocated. Both instructions operate in units of :ref:`page size `. @@ -763,7 +784,7 @@ Instructions in this group affect the flow of control. \CALLREF~\typeidx \\&&|& \CALLINDIRECT~\tableidx~\typeidx \\&&|& \RETURNCALL~\funcidx \\&&|& - \RETURNCALLREF~\funcidx \\&&|& + \RETURNCALLREF~\typeidx \\&&|& \RETURNCALLINDIRECT~\tableidx~\typeidx \\&&|& \THROW~\tagidx \\&&|& \THROWREF \\ &&|& diff --git a/document/core/syntax/modules.rst b/document/core/syntax/modules.rst index 8ffebb9de..a0ea7fe75 100644 --- a/document/core/syntax/modules.rst +++ b/document/core/syntax/modules.rst @@ -445,7 +445,7 @@ The |MIMPORTS| component of a module defines a set of *imports* that are require \IDTABLE~\tabletype \\&&|& \IDMEM~\memtype \\&&|& \IDGLOBAL~\globaltype \\&&|& - \IDTAG~\tagtype \\ + \IDTAG~\typeidx \\ \end{array} Each import is labeled by a two-level :ref:`name ` space, consisting of a |IMODULE| name and a |INAME| for an entity within that module. diff --git a/document/core/syntax/types.rst b/document/core/syntax/types.rst index bbbfd3033..b2fa630f0 100644 --- a/document/core/syntax/types.rst +++ b/document/core/syntax/types.rst @@ -81,6 +81,7 @@ Conventions .. _type-concrete: .. _syntax-i31: .. _syntax-heaptype: +.. _syntax-absheaptype: Heap Types ~~~~~~~~~~ @@ -298,12 +299,21 @@ Structures are heterogeneous, but require static indexing, while arrays need to \end{array} .. _bitwidth-fieldtype: +.. _aux-unpacktype: Conventions ........... * The notation :math:`|t|` for :ref:`bit width ` extends to packed types as well, that is, :math:`|\I8| = 8` and :math:`|\I16| = 16`. +* The auxiliary function :math:`\unpacktype` maps a storage type to the :ref:`value type ` obtained when accessing a field: + + .. math:: + \begin{array}{lll} + \unpacktype(\valtype) &=& \valtype \\ + \unpacktype(\packedtype) &=& \I32 \\ + \end{array} + .. index:: ! composite type, function type, aggreagate type, structure type, array type pair: abstract syntax; composite type @@ -347,6 +357,38 @@ In a :ref:`module `, each member of a recursive type is assigned The syntax of sub types is :ref:`generalized ` for the purpose of specifying :ref:`validation ` and :ref:`execution `. +.. _index:: ! address type + pair: abstract syntax; address type + single: memory; address type + single: table; address type +.. _syntax-addrtype: + +Address Type +~~~~~~~~~~~~ + +*Address types* are a subset of :ref:`number types ` that classify the values that can be used as offsets into +:ref:`memories ` and :ref:`tables `. + +.. math:: + \begin{array}{llll} + \production{address type} & \addrtype &::=& + \I32 ~|~ \I64 \\ + \end{array} + +.. _aux-addrtype-min: + +Conventions +........... + +The *minimum* of two address types is defined as the address type whose :ref:`bit width ` is the minimum of the two. + +.. math:: + \begin{array}{llll} + \addrtypemin(\X{at}_1, \X{at}_2) &=& \X{at}_1 & (\iff |\X{at}_1| \leq |\X{at}_2|) \\ + \addrtypemin(\X{at}_1, \X{at}_2) &=& \X{at}_2 & (\otherwise) \\ + \end{array} + + .. index:: ! limits, memory type, table type pair: abstract syntax; limits single: memory; limits @@ -361,7 +403,7 @@ Limits .. math:: \begin{array}{llrl} \production{limits} & \limits &::=& - \{ \LMIN~\u32, \LMAX~\u32^? \} \\ + \{ \LMIN~\u64, \LMAX~\u64^? \} \\ \end{array} If no maximum is given, the respective storage can grow to any size. @@ -381,7 +423,7 @@ Memory Types .. math:: \begin{array}{llrl} \production{memory type} & \memtype &::=& - \limits \\ + \addrtype~\limits \\ \end{array} The limits constrain the minimum and optionally the maximum size of a memory. @@ -402,7 +444,7 @@ Table Types .. math:: \begin{array}{llrl} \production{table type} & \tabletype &::=& - \limits~\reftype \\ + \addrtype~\limits~\reftype \\ \end{array} Like memories, tables are constrained by limits for their minimum and optionally maximum size. @@ -441,11 +483,11 @@ Global Types Tag Types ~~~~~~~~~ -*Tag types* classify the signature of :ref:`tags ` with a function type. +*Tag types* classify the signature of :ref:`tags ` with a defined type |deftype|, which expands to a function type |functype|. .. math:: \begin{array}{llll} - \production{tag type} &\tagtype &::=& \functype \\ + \production{tag type} &\tagtype &::=& \deftype \\ \end{array} Currently tags are only used for categorizing exceptions. diff --git a/document/core/text/conventions.rst b/document/core/text/conventions.rst index dd172ca0d..ff3720638 100644 --- a/document/core/text/conventions.rst +++ b/document/core/text/conventions.rst @@ -120,22 +120,23 @@ It is convenient to define identifier contexts as :ref:`records ` assigned to the defined indices. +For each index space, such a context contains the list of :ref:`names ` assigned to the defined indices, +which were denoted by the corresponding :ref:`identifiers `. Unnamed indices are associated with empty (:math:`\epsilon`) entries in these lists. Fields have *dependent* name spaces, and hence a separate list of field identifiers per type. diff --git a/document/core/text/instructions.rst b/document/core/text/instructions.rst index d0f911f24..5398e0c69 100644 --- a/document/core/text/instructions.rst +++ b/document/core/text/instructions.rst @@ -32,11 +32,11 @@ The following grammar handles the corresponding update to the :ref:`identifier c .. math:: \begin{array}{llcllll} \production{label} & \Tlabel_I &::=& - v{:}\Tid &\Rightarrow& \{\ILABELS~v\} \compose I + v{:}\Tid &\Rightarrow& v, \{\ILABELS~v\} \compose I & (\iff v \notin I.\ILABELS) \\ &&|& - v{:}\Tid &\Rightarrow& \{\ILABELS~v\} \compose (I \with \ILABELS[i] = \epsilon) + v{:}\Tid &\Rightarrow& v, \{\ILABELS~v\} \compose (I \with \ILABELS[i] = \epsilon) & (\iff I.\ILABELS[i] = v) \\ &&|& - \epsilon &\Rightarrow& \{\ILABELS~(\epsilon)\} \compose I \\ + \epsilon &\Rightarrow& \epsilon, \{\ILABELS~(\epsilon)\} \compose I \\ \end{array} .. note:: @@ -81,16 +81,16 @@ However, the special case of a type use that is syntactically empty or consists x,I'{:}\Ttypeuse_I &\Rightarrow& x & (\iff I' = \{\ILOCALS~(\epsilon)^\ast\}) \\ \end{array} \\ \production{block instruction} & \Tblockinstr_I &::=& - \text{block}~~I'{:}\Tlabel_I~~\X{bt}{:}\Tblocktype_I~~(\X{in}{:}\Tinstr_{I'})^\ast~~\text{end}~~\Tid^? + \text{block}~~(v^?,I'){:}\Tlabel_I~~\X{bt}{:}\Tblocktype_I~~(\X{in}{:}\Tinstr_{I'})^\ast~~\text{end}~~{v'}^?{:}\Tid^? \\ &&&\qquad \Rightarrow\quad \BLOCK~\X{bt}~\X{in}^\ast~\END - \qquad\quad~~ (\iff \Tid^? = \epsilon \vee \Tid^? = \Tlabel) \\ &&|& - \text{loop}~~I'{:}\Tlabel_I~~\X{bt}{:}\Tblocktype_I~~(\X{in}{:}\Tinstr_{I'})^\ast~~\text{end}~~\Tid^? + \qquad\quad~~ (\iff {v'}^? = \epsilon \vee {v'}^? = v^?) \\ &&|& + \text{loop}~~(v^?,I'){:}\Tlabel_I~~\X{bt}{:}\Tblocktype_I~~(\X{in}{:}\Tinstr_{I'})^\ast~~\text{end}~~{v'}^?{:}\Tid^? \\ &&&\qquad \Rightarrow\quad \LOOP~\X{bt}~\X{in}^\ast~\END - \qquad\qquad (\iff \Tid^? = \epsilon \vee \Tid^? = \Tlabel) \\ &&|& - \text{if}~~I'{:}\Tlabel_I~~\X{bt}{:}\Tblocktype_I~~(\X{in}_1{:}\Tinstr_{I'})^\ast~~ - \text{else}~~\Tid_1^?~~(\X{in}_2{:}\Tinstr_{I'})^\ast~~\text{end}~~\Tid_2^? + \qquad\qquad (\iff {v'}^? = \epsilon \vee {v'}^? = v^?) \\ &&|& + \text{if}~~(v^?,I'){:}\Tlabel_I~~\X{bt}{:}\Tblocktype_I~~(\X{in}_1{:}\Tinstr_{I'})^\ast~~ + \text{else}~~v_1^?{:}\Tid_1^?~~(\X{in}_2{:}\Tinstr_{I'})^\ast~~\text{end}~~v_2^?{:}\Tid_2^? \\ &&&\qquad \Rightarrow\quad \IF~\X{bt}~\X{in}_1^\ast~\ELSE~\X{in}_2^\ast~\END - \qquad (\iff \Tid_1^? = \epsilon \vee \Tid_1^? = \Tlabel, \Tid_2^? = \epsilon \vee \Tid_2^? = \Tlabel) \\ &&|& + \qquad (\iff v_1^? = \epsilon \vee v_1^? = v^?, v_2^? = \epsilon \vee v_2^? = v^?) \\ &&|& \text{try\_table}~~I'{:}\Tlabel_I~~\X{bt}{:}\Tblocktype~~(c{:}\Tcatch_I)^\ast~~(\X{in}{:}\Tinstr_{I'})^\ast~~\text{end}~~\Tid^? \\ &&&\qquad \Rightarrow\quad \TRYTABLE~\X{bt}~c^\ast~\X{in}^\ast~~\END \qquad\qquad (\iff \Tid^? = \epsilon \vee \Tid^? = \Tlabel) \\ @@ -950,6 +950,7 @@ Vector constant instructions have a mandatory :ref:`shape ` de \text{i32x4.all\_true} &\Rightarrow& \I32X4.\ALLTRUE\\ &&|& \text{i32x4.bitmask} &\Rightarrow& \I32X4.\BITMASK\\ &&|& \text{i32x4.extadd\_pairwise\_i16x8\_s} &\Rightarrow& \I32X4.\EXTADDPAIRWISE\K{\_i16x8\_s}\\ &&|& + \text{i32x4.extadd\_pairwise\_i16x8\_u} &\Rightarrow& \I32X4.\EXTADDPAIRWISE\K{\_i16x8\_u}\\ &&|& \text{i32x4.extend\_low\_i16x8\_s} &\Rightarrow& \I32X4.\VEXTEND\K{\_low\_i16x8\_s}\\ &&|& \text{i32x4.extend\_high\_i16x8\_s} &\Rightarrow& \I32X4.\VEXTEND\K{\_high\_i16x8\_s}\\ &&|& \text{i32x4.extend\_low\_i16x8\_u} &\Rightarrow& \I32X4.\VEXTEND\K{\_low\_i16x8\_u}\\ &&|& @@ -1052,6 +1053,31 @@ Vector constant instructions have a mandatory :ref:`shape ` de \text{f64x2.promote\_low\_f32x4} &\Rightarrow& \F64X2.\VPROMOTE\K{\_low\_f32x4}\\ \end{array} +.. math:: + \begin{array}{llclll} + \phantom{\production{instruction}} & \phantom{\Tplaininstr_I} &\phantom{::=}& \phantom{averylonginstructionnameforvectext} && \phantom{vechasreallyreallyreallylonginstructionnames} \\[-2ex] &&|& + \text{i16x8.relaxed\_swizzle} &\Rightarrow& \I16X8.\RELAXEDSWIZZLE \\ &&|& + \text{i32x4.relaxed\_trunc\_f32x4\_s} &\Rightarrow& \I32X4.\RELAXEDTRUNC\K{\_f32x4\_s} \\ &&|& + \text{i32x4.relaxed\_trunc\_f32x4\_u} &\Rightarrow& \I32X4.\RELAXEDTRUNC\K{\_f32x4\_u} \\ &&|& + \text{i32x4.relaxed\_trunc\_f32x4\_s\_zero} &\Rightarrow& \I32X4.\RELAXEDTRUNC\K{\_f32x4\_s\_zero} \\ &&|& + \text{i32x4.relaxed\_trunc\_f32x4\_u\_zero} &\Rightarrow& \I32X4.\RELAXEDTRUNC\K{\_f32x4\_u\_zero} \\ &&|& + \text{f32x4.relaxed\_madd} &\Rightarrow& \F32X4.\RELAXEDMADD \\ &&|& + \text{f32x4.relaxed\_nmadd} &\Rightarrow& \F32X4.\RELAXEDNMADD \\ &&|& + \text{f64x2.relaxed\_madd} &\Rightarrow& \F64X2.\RELAXEDMADD \\ &&|& + \text{f64x2.relaxed\_nmadd} &\Rightarrow& \F64X2.\RELAXEDNMADD \\ &&|& + \text{i8x16.relaxed\_laneselect} &\Rightarrow& \I8X16.\RELAXEDLANESELECT \\ &&|& + \text{i16x8.relaxed\_laneselect} &\Rightarrow& \I16X8.\RELAXEDLANESELECT \\ &&|& + \text{i32x4.relaxed\_laneselect} &\Rightarrow& \I32X4.\RELAXEDLANESELECT \\ &&|& + \text{i64x2.relaxed\_laneselect} &\Rightarrow& \I64X2.\RELAXEDLANESELECT \\ &&|& + \text{f32x4.relaxed\_min} &\Rightarrow& \F32X4.\RELAXEDMIN \\ &&|& + \text{f32x4.relaxed\_max} &\Rightarrow& \F32X4.\RELAXEDMAX \\ &&|& + \text{f64x2.relaxed\_min} &\Rightarrow& \F64X2.\RELAXEDMIN \\ &&|& + \text{f64x2.relaxed\_max} &\Rightarrow& \F64X2.\RELAXEDMAX \\ &&|& + \text{i16x8.relaxed\_q15mulr\_s} &\Rightarrow& \I16X8.\RELAXEDQ15MULRS \\ &&|& + \text{i16x8.relaxed\_dot\_i8x16\_i7x16\_s} &\Rightarrow& \I16X8.\RELAXEDDOT\K{\_i8x16\_i7x16\_s} \\ &&|& + \text{i16x8.relaxed\_dot\_i8x16\_i7x16\_add\_s} &\Rightarrow& \I16X8.\RELAXEDDOT\K{\_i8x16\_i7x16\_add\_s} + \end{array} + .. index:: ! folded instruction, S-expression .. _text-foldedinstr: diff --git a/document/core/text/lexical.rst b/document/core/text/lexical.rst index 4584b0c42..16e83dcd0 100644 --- a/document/core/text/lexical.rst +++ b/document/core/text/lexical.rst @@ -50,7 +50,7 @@ The character stream in the source text is divided, from left to right, into a s (\text{a} ~|~ \dots ~|~ \text{z})~\Tidchar^\ast \qquad (\iff~\mbox{occurring as a literal terminal in the grammar}) \\ \production{reserved} & \Treserved &::=& - (\Tidchar ~|~ \Tstring)^+ \\ + (\Tidchar ~|~ \Tstring ~|~ \text{,} ~|~ \text{;} ~|~ \text{[} ~|~ \text{]} ~|~ \text{\{} ~|~ \text{\}})^+ \\ \end{array} Tokens are formed from the input character stream according to the *longest match* rule. @@ -71,13 +71,14 @@ Any token that does not fall into any of the other categories is considered *res .. index:: ! white space, character, ASCII single: text format; white space -.. _text-format: .. _text-space: +.. _text-format: +.. _text-newline: White Space ~~~~~~~~~~~ -*White space* is any sequence of literal space characters, formatting characters, or :ref:`comments `. +*White space* is any sequence of literal space characters, formatting characters, :ref:`comments `, or :ref:`annotations `. The allowed formatting characters correspond to a subset of the |ASCII|_ *format effectors*, namely, *horizontal tabulation* (:math:`\unicode{09}`), *line feed* (:math:`\unicode{0A}`), and *carriage return* (:math:`\unicode{0D}`). .. math:: @@ -126,3 +127,35 @@ The *look-ahead* restrictions on the productions for |Tblockchar| disambiguate t .. note:: Any formatting and control characters are allowed inside comments. + + +.. index:: ! annotation + single: text format; annotation +.. _text-annot: + +Annotations +~~~~~~~~~~~ + +An *annotation* is a bracketed token sequence headed by an *annotation id* of the form :math:`\text{@id}` or :math:`\text{@"..."}`. +No :ref:`space ` is allowed between the opening parenthesis and this id. +Annotations are intended to be used for third-party extensions; +they can appear anywhere in a program but are ignored by the WebAssembly semantics itself, which treats them as :ref:`white space `. + +Annotations can contain other parenthesized token sequences (including nested annotations), as long as they are well-nested. +:ref:`String literals ` and :ref:`comments ` occurring in an annotation must also be properly nested and closed. + +.. math:: + \begin{array}{llclll@{\qquad\qquad}l} + \production{annotation} & \Tannot &::=& + \text{(@}~\Tannotid ~(\Tspace ~|~ \Ttoken)^\ast~\text{)} \\ + \production{annotation identifier} & \Tannotid &::=& + \Tidchar^+ ~|~ \Tname \\ + \end{array} + +.. note:: + The annotation id is meant to be an identifier categorising the extension, and plays a role similar to the name of a :ref:`custom section `. + By convention, annotations corresponding to a custom section should use the custom section's name as an id. + + Implementations are expected to ignore annotations with ids that they do not recognize. + On the other hand, they may impose restrictions on annotations that they do recognize, e.g., requiring a specific structure by superimposing a more concrete grammar. + It is up to an implementation how it deals with errors in such annotations. diff --git a/document/core/text/modules.rst b/document/core/text/modules.rst index 0cb0568af..0ce3583bd 100644 --- a/document/core/text/modules.rst +++ b/document/core/text/modules.rst @@ -307,21 +307,23 @@ An :ref:`element segment ` can be given inline with a table definitio .. math:: \begin{array}{llclll} \production{module field} & - \text{(}~\text{table}~~\Tid^?~~\Treftype~~\text{(}~\text{elem}~~\expr^n{:}\Tvec(\Telemexpr)~\text{)}~\text{)} \quad\equiv \\ & \qquad - \text{(}~\text{table}~~\Tid'~~n~~n~~\Treftype~\text{)} \\ & \qquad - \text{(}~\text{elem}~~\text{(}~\text{table}~~\Tid'~\text{)}~~\text{(}~\text{i32.const}~~\text{0}~\text{)}~~\Treftype~~\Tvec(\Telemexpr)~\text{)} + \text{(}~\text{table}~~\Tid^?~~\Taddrtype^?~~\Treftype~~\text{(}~\text{elem}~~\expr^n{:}\Tvec(\Telemexpr)~\text{)}~\text{)} \quad\equiv \\ & \qquad + \text{(}~\text{table}~~\Tid'~~\Taddrtype^?~~n~~n~~\Treftype~\text{)} \\ & \qquad + \text{(}~\text{elem}~~\text{(}~\text{table}~~\Tid'~\text{)}~~\text{(}~\Taddrtype'\text{.const}~~\text{0}~\text{)}~~\Treftype~~\Tvec(\Telemexpr)~\text{)} \\ & \qquad\qquad - (\iff \Tid^? \neq \epsilon \wedge \Tid' = \Tid^? \vee \Tid^? = \epsilon \wedge \Tid' \idfresh) \\ + (\iff \Tid^? \neq \epsilon \wedge \Tid' = \Tid^? \vee \Tid^? = \epsilon \wedge \Tid' \idfresh, \\ & \qquad\qquad + \iff \Taddrtype? \neq \epsilon \wedge \Taddrtype' = \Taddrtype^? \vee \Taddrtype^? = \epsilon \wedge \Taddrtype' = \text{i32}) \\ \end{array} .. math:: \begin{array}{llclll} \production{module field} & - \text{(}~\text{table}~~\Tid^?~~\Treftype~~\text{(}~\text{elem}~~x^n{:}\Tvec(\Tfuncidx)~\text{)}~\text{)} \quad\equiv \\ & \qquad - \text{(}~\text{table}~~\Tid'~~n~~n~~\Treftype~\text{)} \\ & \qquad - \text{(}~\text{elem}~~\text{(}~\text{table}~~\Tid'~\text{)}~~\text{(}~\text{i32.const}~~\text{0}~\text{)}~~\Treftype~~\Tvec(\text{(}~\text{ref.func}~~\Tfuncidx~\text{)})~\text{)} + \text{(}~\text{table}~~\Tid^?~~\Taddrtype^?~~\Treftype~~\text{(}~\text{elem}~~x^n{:}\Tvec(\Tfuncidx)~\text{)}~\text{)} \quad\equiv \\ & \qquad + \text{(}~\text{table}~~\Tid'~~\Taddrtype^?~~n~~n~~\Treftype~\text{)} \\ & \qquad + \text{(}~\text{elem}~~\text{(}~\text{table}~~\Tid'~\text{)}~~\text{(}~\Taddrtype'\text{.const}~~\text{0}~\text{)}~~\Treftype~~\Tvec(\text{(}~\text{ref.func}~~\Tfuncidx~\text{)})~\text{)} \\ & \qquad\qquad - (\iff \Tid^? \neq \epsilon \wedge \Tid' = \Tid^? \vee \Tid^? = \epsilon \wedge \Tid' \idfresh) \\ + (\iff \Tid^? \neq \epsilon \wedge \Tid' = \Tid^? \vee \Tid^? = \epsilon \wedge \Tid' \idfresh, \\ & \qquad\qquad + \iff \Taddrtype? \neq \epsilon \wedge \Taddrtype' = \Taddrtype^? \vee \Taddrtype^? = \epsilon \wedge \Taddrtype' = \text{i32}) \\ \end{array} Tables can be defined as :ref:`imports ` or :ref:`exports ` inline: @@ -378,11 +380,13 @@ A :ref:`data segment ` can be given inline with a memory definition, .. math:: \begin{array}{llclll} \production{module field} & - \text{(}~\text{memory}~~\Tid^?~~\text{(}~\text{data}~~b^n{:}\Tdatastring~\text{)}~~\text{)} \quad\equiv \\ & \qquad - \text{(}~\text{memory}~~\Tid'~~m~~m~\text{)} \\ & \qquad - \text{(}~\text{data}~~\text{(}~\text{memory}~~\Tid'~\text{)}~~\text{(}~\text{i32.const}~~\text{0}~\text{)}~~\Tdatastring~\text{)} + \text{(}~\text{memory}~~\Tid^?~~\Taddrtype^?~~\text{(}~\text{data}~~b^n{:}\Tdatastring~\text{)}~~\text{)} \quad\equiv \\ & \qquad + \text{(}~\text{memory}~~\Tid'~~\Taddrtype^?~~m~~m~\text{)} \\ & \qquad + \text{(}~\text{data}~~\text{(}~\text{memory}~~\Tid'~\text{)}~~\text{(}~\Taddrtype'\text{.const}~~\text{0}~\text{)}~~\Tdatastring~\text{)} \\ & \qquad\qquad - (\iff \Tid^? \neq \epsilon \wedge \Tid' = \Tid^? \vee \Tid^? = \epsilon \wedge \Tid' \idfresh, m = \F{ceil}(n / 64\,\F{Ki})) \\ + (\iff \Tid^? \neq \epsilon \wedge \Tid' = \Tid^? \vee \Tid^? = \epsilon \wedge \Tid' \idfresh, \\ & \qquad\qquad + \iff \Taddrtype? \neq \epsilon \wedge \Taddrtype' = \Taddrtype^? \vee \Taddrtype^? = \epsilon \wedge \Taddrtype' = \text{i32}, \\ & \qquad\qquad + m = \F{ceil}(n / 64\,\F{Ki})) \\ \end{array} Memories can be defined as :ref:`imports ` or :ref:`exports ` inline: @@ -641,7 +645,7 @@ As another abbreviation, element segments may also be specified inline with :ref single: data; segment .. _text-datastring: .. _text-data: -.. _test-memuse: +.. _text-memuse: Data Segments ~~~~~~~~~~~~~ @@ -759,34 +763,33 @@ The definition of the initial :ref:`identifier context ` :math:`I` \begin{array}{@{}lcl@{\qquad\qquad}l} \F{idc}(\text{(}~\text{rec}~~\Ttypedef^\ast~\text{)}) &=& \bigcompose \F{idc}(\Ttypedef)^\ast \\ - \F{idc}(\text{(}~\text{type}~\Tid^?~\Tsubtype~\text{)}) &=& - \{\ITYPES~(\Tid^?), \IFIELDS~\F{idf}(\Tsubtype), \ITYPEDEFS~\X{st}\} \\ - \F{idc}(\text{(}~\text{func}~\Tid^?~\dots~\text{)}) &=& - \{\IFUNCS~(\Tid^?)\} \\ - \F{idc}(\text{(}~\text{table}~\Tid^?~\dots~\text{)}) &=& - \{\ITABLES~(\Tid^?)\} \\ - \F{idc}(\text{(}~\text{memory}~\Tid^?~\dots~\text{)}) &=& - \{\IMEMS~(\Tid^?)\} \\ - \F{idc}(\text{(}~\text{global}~\Tid^?~\dots~\text{)}) &=& - \{\IGLOBALS~(\Tid^?)\} \\ - \F{idc}(\text{(}~\text{tag}~\Tid^?~\dots~\text{)}) &=& - \{\ITAGS~(\Tid^?)\} \\ - \F{idc}(\text{(}~\text{elem}~\Tid^?~\dots~\text{)}) &=& - \{\IELEM~(\Tid^?)\} \\ - \F{idc}(\text{(}~\text{data}~\Tid^?~\dots~\text{)}) &=& - \{\IDATA~(\Tid^?)\} \\ - \F{idc}(\text{(}~\text{import}~\dots~\text{(}~\text{func}~\Tid^?~\dots~\text{)}~\text{)}) &=& - \{\IFUNCS~(\Tid^?)\} \\ - \F{idc}(\text{(}~\text{import}~\dots~\text{(}~\text{table}~\Tid^?~\dots~\text{)}~\text{)}) &=& - \{\ITABLES~(\Tid^?)\} \\ - \F{idc}(\text{(}~\text{import}~\dots~\text{(}~\text{memory}~\Tid^?~\dots~\text{)}~\text{)}) &=& - \{\IMEMS~(\Tid^?)\} \\ - \F{idc}(\text{(}~\text{import}~\dots~\text{(}~\text{global}~\Tid^?~\dots~\text{)}~\text{)}) &=& - \{\IGLOBALS~(\Tid^?)\} \\ - \F{idc}(\text{(}~\text{import}~\dots~\text{(}~\text{tag}~\Tid^?~\dots~\text{)}~\text{)}) &=& - \{\ITAGS~(\Tid^?)\} \\ + \F{idc}(\text{(}~\text{type}~v^?{:}\Tid^?~\Tsubtype~\text{)}) &=& + \{\ITYPES~(v^?), \IFIELDS~\F{idf}(\Tsubtype), \ITYPEDEFS~\X{st}\} \\ + \F{idc}(\text{(}~\text{func}~v^?{:}\Tid^?~\dots~\text{)}) &=& + \{\IFUNCS~(v^?)\} \\ + \F{idc}(\text{(}~\text{table}~v^?{:}\Tid^?~\dots~\text{)}) &=& + \{\ITABLES~(v^?)\} \\ + \F{idc}(\text{(}~\text{memory}~v^?{:}\Tid^?~\dots~\text{)}) &=& + \{\IMEMS~(v^?)\} \\ + \F{idc}(\text{(}~\text{global}~v^?{:}\Tid^?~\dots~\text{)}) &=& + \{\IGLOBALS~(v^?)\} \\ + \F{idc}(\text{(}~\text{tag}~v^?{:}\Tid^?~\dots~\text{)}) &=& + \{\ITAGS~(v^?)\} \\ + \F{idc}(\text{(}~\text{elem}~v^?{:}\Tid^?~\dots~\text{)}) &=& + \{\IELEM~(v^?)\} \\ + \F{idc}(\text{(}~\text{data}~v^?{:}\Tid^?~\dots~\text{)}) &=& + \{\IDATA~(v^?)\} \\ + \F{idc}(\text{(}~\text{import}~\dots~\text{(}~\text{func}~v^?{:}\Tid^?~\dots~\text{)}~\text{)}) &=& + \{\IFUNCS~(v^?)\} \\ + \F{idc}(\text{(}~\text{import}~\dots~\text{(}~\text{table}~v^?{:}\Tid^?~\dots~\text{)}~\text{)}) &=& + \{\ITABLES~(v^?)\} \\ + \F{idc}(\text{(}~\text{import}~\dots~\text{(}~\text{memory}~v^?{:}\Tid^?~\dots~\text{)}~\text{)}) &=& + \{\IMEMS~(v^?)\} \\ + \F{idc}(\text{(}~\text{import}~\dots~\text{(}~\text{global}~v^?{:}\Tid^?~\dots~\text{)}~\text{)}) &=& + \{\IGLOBALS~(v^?)\} \\ \F{idc}(\text{(}~\dots~\text{)}) &=& - \{\} \\[2ex] + \{\} + \\[2ex] \F{idf}(\text{(}~\text{sub}~\dots~\Tcomptype~\text{)}) &=& \F{idf}(\Tcomptype) \\ \F{idf}(\text{(}~\text{struct}~\X{Tfield}^\ast~\text{)}) &=& @@ -795,8 +798,8 @@ The definition of the initial :ref:`identifier context ` :math:`I` \epsilon \\ \F{idf}(\text{(}~\text{func}~\dots~\text{)}) &=& \epsilon \\ - \F{idf}(\text{(}~\text{field}~\Tid^?~\dots~\text{)}) &=& - \Tid^? \\ + \F{idf}(\text{(}~\text{field}~v^?{:}\Tid^?~\dots~\text{)}) &=& + v^? \\ \end{array} diff --git a/document/core/text/types.rst b/document/core/text/types.rst index 8618f3608..5c7e7386b 100644 --- a/document/core/text/types.rst +++ b/document/core/text/types.rst @@ -286,6 +286,32 @@ Similarly, final sub types with no super-types can omit the |Tsub| keyword and a \end{array} +.. index:: address type + pair: text format; address type +.. _text-addrtype: + +Address Types +~~~~~~~~~~~~~ + +.. math:: + \begin{array}{llclll} + \production{address type} & \Taddrtype &::=& + \text{i32} &\Rightarrow& \I32 \\ &&|& + \text{i64} &\Rightarrow& \I64 \\ + \end{array} + +Abbreviations +............. + +The address type can be omited, in which case it defaults :math:`\I32`: + +.. math:: + \begin{array}{llclll} + \production{address type} & + \text{} &\equiv& \text{i32} + \end{array} + + .. index:: limits pair: text format; limits .. _text-limits: @@ -311,7 +337,7 @@ Memory Types .. math:: \begin{array}{llclll@{\qquad\qquad}l} \production{memory type} & \Tmemtype_I &::=& - \X{lim}{:}\Tlimits &\Rightarrow& \X{lim} \\ + \X{at}{:}\Taddrtype~~\X{lim}{:}\Tlimits &\Rightarrow& \X{at}~\X{lim} \\ \end{array} @@ -325,7 +351,7 @@ Table Types .. math:: \begin{array}{llclll} \production{table type} & \Ttabletype_I &::=& - \X{lim}{:}\Tlimits~~\X{et}{:}\Treftype_I &\Rightarrow& \X{lim}~\X{et} \\ + \X{at}{:}\Taddrtype~~\X{lim}{:}\Tlimits~~\X{et}{:}\Treftype_I &\Rightarrow& \X{at}~\X{lim}~\X{et} \\ \end{array} diff --git a/document/core/text/values.rst b/document/core/text/values.rst index 2b869d251..80a142fa1 100644 --- a/document/core/text/values.rst +++ b/document/core/text/values.rst @@ -211,12 +211,13 @@ Identifiers ~~~~~~~~~~~ :ref:`Indices ` can be given in both numeric and symbolic form. -Symbolic *identifiers* that stand in lieu of indices start with :math:`\text{\$}`, followed by any sequence of printable |ASCII|_ characters that does not contain a space, quotation mark, comma, semicolon, or bracket. +Symbolic *identifiers* that stand in lieu of indices start with :math:`\text{\$}`, followed by eiter a sequence of printable |ASCII|_ characters that does not contain a space, quotation mark, comma, semicolon, or bracket, or by a quoted :ref:`name `. .. math:: \begin{array}{llclll@{\qquad}l} \production{identifier} & \Tid &::=& - \text{\$}~\Tidchar^+ \\ + \text{\$}~c^\ast{:}\Tidchar^+ &\Rightarrow& c^\ast \\ &&|& + \text{\$}~c^\ast{:}\Tname &\Rightarrow& c^\ast & (\iff |c^\ast| > 0) \\ \production{identifier character} & \Tidchar &::=& \text{0} ~~|~~ \dots ~~|~~ \text{9} \\ &&|& \text{A} ~~|~~ \dots ~~|~~ \text{Z} \\ &&|& @@ -246,6 +247,9 @@ Symbolic *identifiers* that stand in lieu of indices start with :math:`\text{\$} \text{\tilde{~~}} \\ \end{array} +.. note:: + The value of an identifier character is the Unicode codepoint denoting it. + .. _text-id-fresh: Conventions diff --git a/document/core/util/bikeshed_fixup.py b/document/core/util/bikeshed_fixup.py index 7041d875d..af9a029d4 100755 --- a/document/core/util/bikeshed_fixup.py +++ b/document/core/util/bikeshed_fixup.py @@ -12,11 +12,7 @@ def Main(): data = open(sys.argv[1]).read() - # Make bikeshed happy - # Apparently it can't handle empty line before DOCTYPE comment - data = data.replace('\n - data = data.replace('
', '\n
')
+  # Clean up the input for Bikeshed
 
   # Don't add more than 3 levels to TOC.
   data = data.replace('
', '
') @@ -29,10 +25,13 @@ def Main(): number = 1 for section in [ 'Embedding', + 'Profiles', 'Implementation Limitations', 'Validation Algorithm', 'Custom Sections', 'Soundness', + 'Type System Properties', + 'Change History', 'Index of Types', 'Index of Instructions', 'Index of Semantic Rules']: @@ -41,18 +40,6 @@ def Main(): '

A.' + str(number) + ' ' + section + '

') number += 1 - - # Drop spurious navigation. - data = data.replace( -""" - """, '') - # Use bikeshed biblio references for unicode and IEEE754 data = data.replace( """Unicode""", @@ -86,6 +73,9 @@ def Main(): r'\1', data, flags=re.DOTALL) + # Escape some latex sequences that Bikeshed interprets as macros + data = data.replace(r' \\[1ex]', r' \\\[1ex]') + sys.stdout.write(data) Main() diff --git a/document/core/util/check_macros.sh b/document/core/util/check_macros.sh new file mode 100644 index 000000000..2ac610129 --- /dev/null +++ b/document/core/util/check_macros.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +cd `dirname $0`/.. + +FILES=`ls */*.rst` +ERRORS=0 + +for XREF in `grep "[\\]xref" util/macros.def`; do + if echo $XREF | grep -q "[|]"; then + MACRO=`echo $XREF | sed 's/^[^|]*[|]//g' | sed 's/[|].*$//g'` + elif echo $XREF | grep -q xref; then + FILE=`echo $XREF | sed 's/^.*xref{//g' | sed 's/}.*$//g'`.rst + LABEL=`echo $XREF | sed 's/^[^}]*}{//g' | sed 's/}.*$//g'` + TARGET=".. _$LABEL:" + if ! [ -f $FILE ] || ! grep -q "$TARGET" $FILE; then + ERRORS=1 + echo Undefined cross-reference $FILE:$LABEL in macro "|$MACRO|" + if ! [ -f $FILE ]; then + echo ...non-existent file $FILE + fi + if grep -q "$TARGET" $FILES; then + echo ...defined in `grep -l "$TARGET" $FILES` + fi + fi + fi +done + +if [ $ERRORS -eq 0 ]; then + echo All cross-references okay. +else + exit 1; +fi diff --git a/document/core/util/macros.def b/document/core/util/macros.def index dccb86169..92f4e5490 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -101,7 +101,7 @@ .. Notation for Sequences & Records -.. |subst| mathdef:: \xref{syntax/conventions}{notation-subst}{\mathrel{\mathbf{:=}}} +.. |subst| mathdef:: \xref{valid/conventions}{notation-subst}{\mathrel{\mathbf{:=}}} .. |slice| mathdef:: \xref{syntax/conventions}{notation-slice}{\mathrel{\mathbf{:}}} .. |with| mathdef:: \xref{syntax/conventions}{notation-replace}{\mathrel{\mbox{with}}} .. |concat| mathdef:: \xref{syntax/conventions}{notation-concat}{\F{concat}} @@ -109,6 +109,18 @@ .. |bigcompose| mathdef:: \xref{syntax/conventions}{notation-compose}{\bigoplus} +.. Profile Annotations +.. ------------------- + +.. |exprofiles#1| mathdef:: {^{\small{[!#1]}}} +.. |profilename#1| mathdef:: {\small{\mathrm{#1}}} +.. |profile| mathdef:: \mathrm + +.. |PROFFULL| mathdef:: \xref{appendix/profiles}{profile-full}{\profile{FUL}} +.. |PROFDET| mathdef:: \xref{appendix/profiles}{profile-deterministic}{\profile{DET}} +.. |EXPROFDET| mathdef:: \exprofiles{\PROFDET} + + .. Abstract Syntax .. --------------- @@ -178,13 +190,13 @@ .. Types, terminals .. |toF| mathdef:: \xref{syntax/types}{syntax-functype}{\rightarrow} -.. |to| mathdef:: \mathrel{\xref{syntax/types}{syntax-instrtype}{\rightarrow}} +.. |to| mathdef:: \mathrel{\xref{valid/conventions}{syntax-instrtype}{\rightarrow}} .. |BOTH| mathdef:: \xref{valid/conventions}{syntax-heaptype-ext}{\K{bot}} .. |BOT| mathdef:: \xref{valid/conventions}{syntax-valtype-ext}{\K{bot}} -.. |I8| mathdef:: \xref{syntax/runtime}{syntax-storagetype}{\K{i8}} -.. |I16| mathdef:: \xref{syntax/runtime}{syntax-storagetype}{\K{i16}} +.. |I8| mathdef:: \xref{syntax/types}{syntax-storagetype}{\K{i8}} +.. |I16| mathdef:: \xref{syntax/types}{syntax-storagetype}{\K{i16}} .. |I32| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{i32}} .. |I64| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{i64}} .. |F32| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{f32}} @@ -239,8 +251,8 @@ .. |MVAR| mathdef:: \xref{syntax/types}{syntax-mut}{\K{var}} .. |MCONST| mathdef:: \xref{syntax/types}{syntax-mut}{\K{const}} -.. |SET| mathdef:: \xref{syntax/types}{syntax-init}{\K{set}} -.. |UNSET| mathdef:: \xref{syntax/types}{syntax-init}{\K{unset}} +.. |SET| mathdef:: \xref{valid/conventions}{syntax-init}{\K{set}} +.. |UNSET| mathdef:: \xref{valid/conventions}{syntax-init}{\K{unset}} .. |LMIN| mathdef:: \xref{syntax/types}{syntax-limits}{\K{min}} .. |LMAX| mathdef:: \xref{syntax/types}{syntax-limits}{\K{max}} @@ -274,12 +286,13 @@ .. |globaltype| mathdef:: \xref{syntax/types}{syntax-globaltype}{\X{globaltype}} .. |tabletype| mathdef:: \xref{syntax/types}{syntax-tabletype}{\X{tabletype}} +.. |addrtype| mathdef:: \xref{syntax/types}{syntax-addrtype}{\X{addrtype}} .. |memtype| mathdef:: \xref{syntax/types}{syntax-memtype}{\X{memtype}} .. |tagtype| mathdef:: \xref{syntax/types}{syntax-tagtype}{\X{tagtype}} .. |limits| mathdef:: \xref{syntax/types}{syntax-limits}{\X{limits}} .. |mut| mathdef:: \xref{syntax/types}{syntax-mut}{\X{mut}} -.. |init| mathdef:: \xref{syntax/types}{syntax-init}{\X{init}} +.. |init| mathdef:: \xref{valid/conventions}{syntax-init}{\X{init}} .. |instrtype| mathdef:: \xref{valid/conventions}{syntax-instrtype}{\X{instrtype}} .. |localtype| mathdef:: \xref{valid/conventions}{syntax-localtype}{\X{localtype}} @@ -288,6 +301,7 @@ .. Types, meta functions +.. |addrtypemin| mathdef:: \xref{syntax/types}{aux-addrtype-min}{\F{min}} .. |reftypediff| mathdef:: \xref{valid/conventions}{aux-reftypediff}{\setminus} .. |rollrt| mathdef:: \xref{valid/conventions}{aux-roll-rectype}{\F{roll}} @@ -297,7 +311,6 @@ .. |expanddt| mathdef:: \xref{valid/conventions}{aux-expand-deftype}{\F{expand}} .. |unrollht| mathdef:: \xref{appendix/properties}{aux-unroll-heaptype}{\F{unroll}} -.. |packtype| mathdef:: \xref{syntax/types}{aux-packtype}{\F{pack}} .. |unpacktype| mathdef:: \xref{syntax/types}{aux-unpacktype}{\F{unpack}} .. |etfuncs| mathdef:: \xref{syntax/types}{syntax-externtype}{\F{funcs}} @@ -519,10 +532,10 @@ .. |ARRAYGETU| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.get\_u}} .. |ARRAYSET| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.set}} .. |ARRAYLEN| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.len}} -.. |ARRAYFILL| mathdef:: \xref{syntax/instructions}{syntax-instr-aray}{\K{array.fill}} -.. |ARRAYCOPY| mathdef:: \xref{syntax/instructions}{syntax-instr-aray}{\K{array.copy}} -.. |ARRAYINITDATA| mathdef:: \xref{syntax/instructions}{syntax-instr-aray}{\K{array.init\_data}} -.. |ARRAYINITELEM| mathdef:: \xref{syntax/instructions}{syntax-instr-aray}{\K{array.init\_elem}} +.. |ARRAYFILL| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.fill}} +.. |ARRAYCOPY| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.copy}} +.. |ARRAYINITDATA| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.init\_data}} +.. |ARRAYINITELEM| mathdef:: \xref{syntax/instructions}{syntax-instr-array}{\K{array.init\_elem}} .. |REFI31| mathdef:: \xref{syntax/instructions}{syntax-instr-i31}{\K{ref.i31}} .. |I31GET| mathdef:: \xref{syntax/instructions}{syntax-instr-i31}{\K{i31.get}} @@ -622,6 +635,15 @@ .. |EXTADDPAIRWISE| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{extadd\_pairwise}} .. |VDEMOTE| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{demote}} .. |VPROMOTE| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{promote}} +.. |RELAXEDMADD| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{relaxed\_madd}} +.. |RELAXEDNMADD| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{relaxed\_nmadd}} +.. |RELAXEDSWIZZLE| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{relaxed\_swizzle}} +.. |RELAXEDLANESELECT| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{relaxed\_laneselect}} +.. |RELAXEDMIN| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{relaxed\_min}} +.. |RELAXEDMAX| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{relaxed\_max}} +.. |RELAXEDQ15MULRS| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{relaxed\_q15mulr\_s}} +.. |RELAXEDTRUNC| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{relaxed\_trunc}} +.. |RELAXEDDOT| mathdef:: \xref{syntax/instructions}{syntax-instr-vec}{\K{relaxed\_dot}} .. Instructions, non-terminals @@ -654,10 +676,9 @@ .. |vunop| mathdef:: \xref{syntax/instructions}{syntax-vunop}{\X{vunop}} .. |vbinop| mathdef:: \xref{syntax/instructions}{syntax-vbinop}{\X{vbinop}} -.. |vrelop| mathdef:: \xref{syntax/instructions}{syntax-vrelop}{\X{vrelop}} .. |vternop| mathdef:: \xref{syntax/instructions}{syntax-vternop}{\X{vternop}} +.. |vrelop| mathdef:: \xref{syntax/instructions}{syntax-vrelop}{\X{vrelop}} .. |vcvtop| mathdef:: \xref{syntax/instructions}{syntax-vcvtop}{\X{vcvtop}} -.. |vextmul| mathdef:: \xref{syntax/instructions}{syntax-vextmul}{\X{vextmul}} .. |laneidx| mathdef:: \xref{syntax/instructions}{syntax-laneidx}{\X{laneidx}} .. |vvunop| mathdef:: \xref{syntax/instructions}{syntax-vvunop}{\X{vvunop}} @@ -675,6 +696,8 @@ .. |vfrelop| mathdef:: \xref{syntax/instructions}{syntax-vfrelop}{\X{vfrelop}} .. |vitestop| mathdef:: \xref{syntax/instructions}{syntax-vitestop}{\X{vitestop}} .. |vtestop| mathdef:: \xref{syntax/instructions}{syntax-vtestop}{\X{vtestop}} +.. |rvfbinop| mathdef:: \xref{syntax/instructions}{syntax-rvfbinop}{\X{rvfbinop}} +.. |rvfternop| mathdef:: \xref{syntax/instructions}{syntax-rvfternop}{\X{rvfternop}} .. |sx| mathdef:: \xref{syntax/instructions}{syntax-sx}{\X{sx}} .. |half| mathdef:: \xref{syntax/instructions}{syntax-half}{\X{half}} @@ -874,6 +897,10 @@ .. |Tlinechar| mathdef:: \xref{text/lexical}{text-comment}{\T{linechar}} .. |Tblockchar| mathdef:: \xref{text/lexical}{text-comment}{\T{blockchar}} +.. |Tannot| mathdef:: \xref{text/lexical}{text-annot}{\T{annot}} +.. |Tannotid| mathdef:: \xref{text/lexical}{text-annot}{\T{annotid}} +.. |Tannottoken| mathdef:: \xref{text/lexical}{text-annot}{\T{annottoken}} + .. Values, non-terminals @@ -934,7 +961,7 @@ .. |Tfunctype| mathdef:: \xref{text/types}{text-functype}{\T{functype}} .. |Tstructtype| mathdef:: \xref{text/types}{text-structtype}{\T{structtype}} .. |Tarraytype| mathdef:: \xref{text/types}{text-arraytype}{\T{arraytype}} -.. |Taggrtype| mathdef:: \xref{text/types}{text-aggrype}{\T{aggrtype}} +.. |Taggrtype| mathdef:: \xref{text/types}{text-aggrtype}{\T{aggrtype}} .. |Tfieldtype| mathdef:: \xref{text/types}{text-fieldtype}{\T{fieldtype}} .. |Tstoragetype| mathdef:: \xref{text/types}{text-storagetype}{\T{storagetype}} .. |Tpackedtype| mathdef:: \xref{text/types}{text-packedtype}{\T{packedtype}} @@ -945,6 +972,7 @@ .. |Tglobaltype| mathdef:: \xref{text/types}{text-globaltype}{\T{globaltype}} .. |Ttabletype| mathdef:: \xref{text/types}{text-tabletype}{\T{tabletype}} +.. |Taddrtype| mathdef:: \xref{text/types}{text-addrtype}{\T{addrtype}} .. |Tmemtype| mathdef:: \xref{text/types}{text-memtype}{\T{memtype}} .. |Tlimits| mathdef:: \xref{text/types}{text-limits}{\T{limits}} @@ -971,9 +999,8 @@ .. Modules, non-terminals .. |Tmodule| mathdef:: \xref{text/modules}{text-module}{\T{module}} -.. |Tmodulebody| mathdef:: \xref{text/modules}{text-modulebody}{\T{modulebody}} .. |Tmodulefield| mathdef:: \xref{text/modules}{text-modulefield}{\T{modulefield}} -.. |Ttype| mathdef:: \xref{text/modules}{text-typedef}{\T{type}} +.. |Ttype| mathdef:: \xref{text/types}{text-typedef}{\T{type}} .. |Ttypeuse| mathdef:: \xref{text/modules}{text-typeuse}{\T{typeuse}} .. |Tfunc| mathdef:: \xref{text/modules}{text-func}{\T{func}} .. |Ttable| mathdef:: \xref{text/modules}{text-table}{\T{table}} @@ -988,7 +1015,6 @@ .. |Telemlist| mathdef:: \xref{text/modules}{text-elemlist}{\T{elemlist}} .. |Telemexpr| mathdef:: \xref{text/modules}{text-elemexpr}{\T{elemexpr}} .. |Ttableuse| mathdef:: \xref{text/modules}{text-tableuse}{\T{tableuse}} -.. |Tcode| mathdef:: \xref{text/modules}{text-code}{\T{code}} .. |Tlocal| mathdef:: \xref{text/modules}{text-local}{\T{local}} .. |Tlocals| mathdef:: \xref{text/modules}{text-local}{\T{locals}} .. |Tdata| mathdef:: \xref{text/modules}{text-data}{\T{data}} @@ -1077,7 +1103,7 @@ .. Meta functions -.. |clostype| mathdef:: \xref{valid/conventions}{closure}{\K{clos}} +.. |clostype| mathdef:: \xref{valid/conventions}{aux-clostype}{\K{clos}} .. Contexts @@ -1094,7 +1120,7 @@ .. |CLABELS| mathdef:: \xref{valid/conventions}{context}{\K{labels}} .. |CRETURN| mathdef:: \xref{valid/conventions}{context}{\K{return}} .. |CREFS| mathdef:: \xref{valid/conventions}{context}{\K{refs}} -.. |CRECS| mathdef:: \xref{valid/matching}{context-rec}{\K{recs}} +.. |CRECS| mathdef:: \xref{appendix/properties}{context-ext}{\K{recs}} .. Judgments @@ -1175,7 +1201,7 @@ .. |vdashimportdesc| mathdef:: \xref{valid/modules}{valid-importdesc}{\vdash} .. |vdashmodule| mathdef:: \xref{valid/modules}{valid-module}{\vdash} -.. |unpacked| mathdef:: \xref{valid/instructions}{aux-unpacked}{\F{unpacked}} +.. |unpackshape| mathdef:: \xref{valid/instructions}{aux-unpackshape}{\F{unpack}} .. |dim| mathdef:: \xref{valid/instructions}{aux-dim}{\F{dim}} @@ -1184,7 +1210,7 @@ .. Notation -.. |stepto| mathdef:: \xref{exec/conventions}{formal-notation}{\hookrightarrow} +.. |stepto| mathdef:: \xref{exec/conventions}{exec-notation}{\hookrightarrow} .. |extendsto| mathdef:: \xref{appendix/properties}{extend}{\preceq} @@ -1225,13 +1251,13 @@ .. Address, meta functions -.. |freefuncaddr| mathdef:: \xref{syntax/modules}{syntax-funcaddr}{\F{funcaddr}} -.. |freetableaddr| mathdef:: \xref{syntax/modules}{syntax-tableaddr}{\F{tableaddr}} -.. |freememaddr| mathdef:: \xref{syntax/modules}{syntax-memaddr}{\F{memaddr}} -.. |freeglobaladdr| mathdef:: \xref{syntax/modules}{syntax-globaladdr}{\F{globaladdr}} -.. |freetagaddr| mathdef:: \xref{syntax/modules}{syntax-globaladdr}{\F{tagaddr}} -.. |freeelemaddr| mathdef:: \xref{syntax/modules}{syntax-elemaddr}{\F{elemaddr}} -.. |freedataaddr| mathdef:: \xref{syntax/modules}{syntax-dataaddr}{\F{dataaddr}} +.. |freefuncaddr| mathdef:: \xref{exec/runtime}{syntax-funcaddr}{\F{funcaddr}} +.. |freetableaddr| mathdef:: \xref{exec/runtime}{syntax-tableaddr}{\F{tableaddr}} +.. |freememaddr| mathdef:: \xref{exec/runtime}{syntax-memaddr}{\F{memaddr}} +.. |freeglobaladdr| mathdef:: \xref{exec/runtime}{syntax-globaladdr}{\F{globaladdr}} +.. |freetagaddr| mathdef:: \xref{exec/runtime}{syntax-globaladdr}{\F{tagaddr}} +.. |freeelemaddr| mathdef:: \xref{exec/runtime}{syntax-elemaddr}{\F{elemaddr}} +.. |freedataaddr| mathdef:: \xref{exec/runtime}{syntax-dataaddr}{\F{dataaddr}} .. Instances, terminals @@ -1286,8 +1312,8 @@ .. |EIFIELDS| mathdef:: \xref{exec/runtime}{syntax-exninst}{\K{fields}} .. |PACK| mathdef:: \xref{exec/runtime}{syntax-packedval}{\K{pack}} -.. |I8PACK| mathdef:: \xref{exec/runtime}{syntax-packval}{\K{i8.pack}} -.. |I16PACK| mathdef:: \xref{exec/runtime}{syntax-packval}{\K{i16.pack}} +.. |I8PACK| mathdef:: \xref{exec/runtime}{syntax-packedval}{\K{i8.pack}} +.. |I16PACK| mathdef:: \xref{exec/runtime}{syntax-packedval}{\K{i16.pack}} .. Instances, non-terminals @@ -1380,7 +1406,7 @@ .. Values & Results, non-terminals .. |num| mathdef:: \xref{exec/runtime}{syntax-num}{\X{num}} -.. |vecc| mathdef:: \xref{exec/runtime}{syntax-vec}{\X{vec}} +.. |vecc| mathdef:: \xref{exec/runtime}{syntax-vecc}{\X{vec}} .. |reff| mathdef:: \xref{exec/runtime}{syntax-ref}{\X{ref}} .. |val| mathdef:: \xref{exec/runtime}{syntax-val}{\X{val}} .. |result| mathdef:: \xref{exec/runtime}{syntax-result}{\X{result}} @@ -1451,10 +1477,10 @@ .. |imins| mathdef:: \xref{exec/numerics}{op-imin_s}{\F{imin\_s}} .. |imaxu| mathdef:: \xref{exec/numerics}{op-imax_u}{\F{imax\_u}} .. |imaxs| mathdef:: \xref{exec/numerics}{op-imax_s}{\F{imax\_s}} -.. |iaddsatu| mathdef:: \xref{exec/numerics}{op-iaddsat_u}{\F{iaddsat\_u}} -.. |iaddsats| mathdef:: \xref{exec/numerics}{op-iaddsat_s}{\F{iaddsat\_s}} -.. |isubsatu| mathdef:: \xref{exec/numerics}{op-isubsat_u}{\F{isubsat\_u}} -.. |isubsats| mathdef:: \xref{exec/numerics}{op-isubsat_s}{\F{isubsat\_s}} +.. |iaddsatu| mathdef:: \xref{exec/numerics}{op-iadd_sat_u}{\F{iadd\_sat\_u}} +.. |iaddsats| mathdef:: \xref{exec/numerics}{op-iadd_sat_s}{\F{iadd\_sat\_s}} +.. |isubsatu| mathdef:: \xref{exec/numerics}{op-isub_sat_u}{\F{isub\_sat\_u}} +.. |isubsats| mathdef:: \xref{exec/numerics}{op-isub_sat_s}{\F{isub\_sat\_s}} .. |iavgru| mathdef:: \xref{exec/numerics}{op-iavgr_u}{\F{iavgr\_u}} .. |iq15mulrsats| mathdef:: \xref{exec/numerics}{op-iq15mulrsat_s}{\F{iq15mulrsat\_s}} @@ -1462,6 +1488,7 @@ .. |fsub| mathdef:: \xref{exec/numerics}{op-fsub}{\F{fsub}} .. |fmul| mathdef:: \xref{exec/numerics}{op-fmul}{\F{fmul}} .. |fdiv| mathdef:: \xref{exec/numerics}{op-fdiv}{\F{fdiv}} +.. |fma| mathdef:: \xref{exec/numerics}{op-fma}{\F{fma}} .. |fmin| mathdef:: \xref{exec/numerics}{op-fmin}{\F{fmin}} .. |fmax| mathdef:: \xref{exec/numerics}{op-fmax}{\F{fmax}} .. |fcopysign| mathdef:: \xref{exec/numerics}{op-fcopysign}{\F{fcopysign}} @@ -1498,6 +1525,17 @@ .. |narrowu| mathdef:: \xref{exec/numerics}{op-narrow_u}{\F{narrow}^{\K{u}}} .. |narrows| mathdef:: \xref{exec/numerics}{op-narrow_s}{\F{narrow}^{\K{s}}} +.. |frelaxedmadd| mathdef:: \xref{exec/numerics}{op-frelaxed_madd}{\F{frelaxed\_madd}} +.. |frelaxednmadd| mathdef:: \xref{exec/numerics}{op-frelaxed_nmadd}{\F{frelaxed\_nmadd}} +.. |irelaxedswizzlelane| mathdef:: \xref{exec/numerics}{op-irelaxed_swizzle_lane}{\F{frelaxed\_swizzle\_lane}} +.. |irelaxedswizzle| mathdef:: \xref{exec/numerics}{op-irelaxed_swizzle}{\F{frelaxed\_swizzle}} +.. |relaxedtrunc| mathdef:: \xref{exec/numerics}{op-relaxed_trunc}{\F{relaxed\_trunc}} +.. |irelaxedlaneselect| mathdef:: \xref{exec/numerics}{op-irelaxed_laneselect}{\F{irelaxed\_laneselect}} +.. |frelaxedmin| mathdef:: \xref{exec/numerics}{op-frelaxed_min}{\F{frelaxed\_min}} +.. |frelaxedmax| mathdef:: \xref{exec/numerics}{op-frelaxed_max}{\F{frelaxed\_max}} +.. |irelaxedq15mulrs| mathdef:: \xref{exec/numerics}{op-irelaxed_q15mulr_s}{\F{irelaxed\_q15mulr\_s}} +.. |irelaxeddotmul| mathdef:: \xref{exec/numerics}{op-irelaxed_dot_mul}{\F{irelaxed\_dot\_mul}} + .. Numerics, meta functions @@ -1519,6 +1557,8 @@ .. |lanes| mathdef:: \xref{exec/numerics}{aux-lanes}{\F{lanes}} +.. |relaxed| mathdef:: \xref{exec/numerics}{aux-relaxed}{\F{relaxed}} + .. Other meta functions @@ -1597,6 +1637,28 @@ .. |Bfieldnamesubsec| mathdef:: \xref{appendix/custom}{binary-fieldnamesec}{\B{fieldnamesubsec}} +.. Annotations +.. ----------- + +.. Custom annotations, non-terminals + +.. |Tcustomannot| mathdef:: \xref{appendix/custom}{text-customannot}{\T{customannot}} +.. |Tcustomplace| mathdef:: \xref{appendix/custom}{text-customannot}{\T{customplace}} +.. |Tsec| mathdef:: \xref{appendix/custom}{text-customannot}{\T{sec}} + + +.. Name annotations, non-terminals + +.. |Tnameannot| mathdef:: \xref{appendix/custom}{text-nameannot}{\T{nameannot}} +.. |Tmodulenameannot| mathdef:: \xref{appendix/custom}{text-modulenameannot}{\T{modulenameannot}} +.. |Tfuncnameannot| mathdef:: \xref{appendix/custom}{text-funcnameannot}{\T{funcnameannot}} +.. |Tparamnameannot| mathdef:: \xref{appendix/custom}{text-paramnameannot}{\T{paramnameannot}} +.. |Tlocalnameannot| mathdef:: \xref{appendix/custom}{text-localnameannot}{\T{localnameannot}} +.. |Ttypenameannot| mathdef:: \xref{appendix/custom}{text-typenameannot}{\T{typenameannot}} +.. |Tfieldnameannot| mathdef:: \xref{appendix/custom}{text-fieldnameannot}{\T{fieldnameannot}} +.. |Ttagnameannot| mathdef:: \xref{appendix/custom}{text-tagnameannot}{\T{tagnameannot}} + + .. Embedding .. --------- @@ -1606,3 +1668,5 @@ .. |error| mathdef:: \xref{appendix/embedding}{embed-error}{\X{error}} .. |ERROR| mathdef:: \xref{appendix/embedding}{embed-error}{\K{error}} +.. |exception| mathdef:: \xref{appendix/embedding}{embed-error}{\X{exception}} +.. |ETHROW| mathdef:: \xref{appendix/embedding}{embed-error}{\K{THROW}} diff --git a/document/core/valid/conventions.rst b/document/core/valid/conventions.rst index bc6a02f40..a2a555c90 100644 --- a/document/core/valid/conventions.rst +++ b/document/core/valid/conventions.rst @@ -303,11 +303,12 @@ In addition to field access written :math:`C.\K{field}` the following notation i .. index:: ! type closure .. _type-closure: +.. _aux-clostype: Convention .......... -Any form of :ref:`type ` can be *closed* to bring it into :ref:`closed ` form relative to a :ref:`context ` it is :ref:`valid ` in by :ref:`substituting ` each :ref:`type index ` :math:`x` occurring in it with the corresponding :ref:`defined type ` :math:`C.\CTYPES[x]`, after first closing the the types in :math:`C.\CTYPES` themselves. +Any form of :ref:`type ` can be *closed* to bring it into :ref:`closed ` form relative to a :ref:`context ` it is :ref:`valid ` in by :ref:`substituting ` each :ref:`type index ` :math:`x` occurring in it with the corresponding :ref:`defined type ` :math:`C.\CTYPES[x]`, after first closing the types in :math:`C.\CTYPES` themselves. .. math:: \begin{array}{@{}lcll@{}} diff --git a/document/core/valid/instructions.rst b/document/core/valid/instructions.rst index 69ab85d05..8d179ff83 100644 --- a/document/core/valid/instructions.rst +++ b/document/core/valid/instructions.rst @@ -379,7 +379,7 @@ Aggregate Reference Instructions \qquad \X{ft}^\ast[y] = \mut~\X{st} \qquad - \sx = \epsilon \Leftrightarrow \X{st} = \unpacktype(\X{st}) + \sx^? = \epsilon \Leftrightarrow \X{st} = \unpacktype(\X{st}) }{ C \vdashinstr \STRUCTGET\K{\_}\sx^?~x~y : [(\REF~\NULL~x)] \to [\unpacktype(\X{st})] } @@ -508,7 +508,7 @@ Aggregate Reference Instructions \qquad C \vdashreftypematch C.\CELEMS[y] \matchesreftype \X{rt} }{ - C \vdashinstr \ARRAYNEWELEM~x~n : [\I32~\I32] \to [(\REF~x)] + C \vdashinstr \ARRAYNEWELEM~x~y : [\I32~\I32] \to [(\REF~x)] } @@ -539,7 +539,7 @@ Aggregate Reference Instructions \qquad C.\CDATAS[y] = {\ok} }{ - C \vdashinstr \ARRAYNEWDATA~x~n : [\I32~\I32] \to [(\REF~x)] + C \vdashinstr \ARRAYNEWDATA~x~y : [\I32~\I32] \to [(\REF~x)] } @@ -566,7 +566,7 @@ Aggregate Reference Instructions \frac{ \expanddt(C.\CTYPES[x]) = \TARRAY~(\mut~\X{st}) \qquad - \sx = \epsilon \Leftrightarrow \X{st} = \unpacktype(\X{st}) + \sx^? = \epsilon \Leftrightarrow \X{st} = \unpacktype(\X{st}) }{ C \vdashinstr \ARRAYGET\K{\_}\sx^?~x : [(\REF~\NULL~x)~\I32] \to [\unpacktype(\X{st})] } @@ -806,7 +806,7 @@ External Reference Instructions single: abstract syntax; instruction .. _valid-instr-vec: -.. _aux-unpacked: +.. _aux-unpackshape: Vector Instructions ~~~~~~~~~~~~~~~~~~~ @@ -815,9 +815,7 @@ Vector instructions can have a prefix to describe the :ref:`shape ` :math:`C.\CTABLES[x]`. +* Let :math:`\X{at}~\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. -* Then the instruction is valid with type :math:`[\I32] \to [t]`. +* Then the instruction is valid with type :math:`[\X{at}] \to [t]`. .. math:: \frac{ - C.\CTABLES[x] = \limits~t + C.\CTABLES[x] = \X{at}~\limits~t }{ - C \vdashinstr \TABLEGET~x : [\I32] \to [t] + C \vdashinstr \TABLEGET~x : [\X{at}] \to [t] } @@ -1349,15 +1414,15 @@ Table Instructions * The table :math:`C.\CTABLES[x]` must be defined in the context. -* Let :math:`\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. +* Let :math:`\X{at}~\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. -* Then the instruction is valid with type :math:`[\I32~t] \to []`. +* Then the instruction is valid with type :math:`[\X{at}~t] \to []`. .. math:: \frac{ - C.\CTABLES[x] = \limits~t + C.\CTABLES[x] = \X{at}~\limits~t }{ - C \vdashinstr \TABLESET~x : [\I32~t] \to [] + C \vdashinstr \TABLESET~x : [\X{at}~t] \to [] } @@ -1368,13 +1433,15 @@ Table Instructions * The table :math:`C.\CTABLES[x]` must be defined in the context. -* Then the instruction is valid with type :math:`[] \to [\I32]`. +* Let :math:`\X{at}~\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. + +* Then the instruction is valid with type :math:`[] \to [\X{at}]`. .. math:: \frac{ - C.\CTABLES[x] = \tabletype + C.\CTABLES[x] = \X{at}~\limits~t }{ - C \vdashinstr \TABLESIZE~x : [] \to [\I32] + C \vdashinstr \TABLESIZE~x : [] \to [\X{at}] } @@ -1385,15 +1452,15 @@ Table Instructions * The table :math:`C.\CTABLES[x]` must be defined in the context. -* Let :math:`\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. +* Let :math:`\X{at}~\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. -* Then the instruction is valid with type :math:`[t~\I32] \to [\I32]`. +* Then the instruction is valid with type :math:`[t~\X{at}] \to [\X{at}]`. .. math:: \frac{ - C.\CTABLES[x] = \limits~t + C.\CTABLES[x] = \X{at}~\limits~t }{ - C \vdashinstr \TABLEGROW~x : [t~\I32] \to [\I32] + C \vdashinstr \TABLEGROW~x : [t~\X{at}] \to [\X{at}] } @@ -1404,15 +1471,15 @@ Table Instructions * The table :math:`C.\CTABLES[x]` must be defined in the context. -* Let :math:`\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. +* Let :math:`\X{at}~\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. -* Then the instruction is valid with type :math:`[\I32~t~\I32] \to []`. +* Then the instruction is valid with type :math:`[\X{at}~t~\X{at}] \to []`. .. math:: \frac{ - C.\CTABLES[x] = \limits~t + C.\CTABLES[x] = \X{at}~\limits~t }{ - C \vdashinstr \TABLEFILL~x : [\I32~t~\I32] \to [] + C \vdashinstr \TABLEFILL~x : [\X{at}~t~\X{at}] \to [] } @@ -1423,25 +1490,27 @@ Table Instructions * The table :math:`C.\CTABLES[x]` must be defined in the context. -* Let :math:`\limits_1~t_1` be the :ref:`table type ` :math:`C.\CTABLES[x]`. +* Let :math:`\X{at}_1~\limits_1~t_1` be the :ref:`table type ` :math:`C.\CTABLES[x]`. * The table :math:`C.\CTABLES[y]` must be defined in the context. -* Let :math:`\limits_2~t_2` be the :ref:`table type ` :math:`C.\CTABLES[y]`. +* Let :math:`\X{at}_2~\limits_2~t_2` be the :ref:`table type ` :math:`C.\CTABLES[y]`. * The :ref:`reference type ` :math:`t_2` must :ref:`match ` :math:`t_1`. -* Then the instruction is valid with type :math:`[\I32~\I32~\I32] \to []`. +* Let :math:`\X{at}` be the :ref:`minimum ` of :math:`\X{at}_1` and :math:`\X{at}_2` + +* Then the instruction is valid with type :math:`[\X{at}_1~\X{at}_2~\X{at}] \to []`. .. math:: \frac{ - C.\CTABLES[x] = \limits_1~t_1 + C.\CTABLES[x] = \X{at}~\limits_1~t_1 \qquad - C.\CTABLES[y] = \limits_2~t_2 + C.\CTABLES[y] = \X{at}~\limits_2~t_2 \qquad C \vdashreftypematch t_2 \matchesvaltype t_1 }{ - C \vdashinstr \TABLECOPY~x~y : [\I32~\I32~\I32] \to [] + C \vdashinstr \TABLECOPY~x~y : [\X{at}_1~\X{at}_2~\addrtypemin(\X{at}_1, \X{at}_2)] \to [] } @@ -1452,7 +1521,7 @@ Table Instructions * The table :math:`C.\CTABLES[x]` must be defined in the context. -* Let :math:`\limits~t_1` be the :ref:`table type ` :math:`C.\CTABLES[x]`. +* Let :math:`\X{at}~\limits~t_1` be the :ref:`table type ` :math:`C.\CTABLES[x]`. * The element segment :math:`C.\CELEMS[y]` must be defined in the context. @@ -1460,17 +1529,17 @@ Table Instructions * The :ref:`reference type ` :math:`t_2` must :ref:`match ` :math:`t_1`. -* Then the instruction is valid with type :math:`[\I32~\I32~\I32] \to []`. +* Then the instruction is valid with type :math:`[\X{at}~\I32~\I32] \to []`. .. math:: \frac{ - C.\CTABLES[x] = \limits~t_1 + C.\CTABLES[x] = \X{at}~\limits~t_1 \qquad C.\CELEMS[y] = t_2 \qquad C \vdashreftypematch t_2 \matchesvaltype t_1 }{ - C \vdashinstr \TABLEINIT~x~y : [\I32~\I32~\I32] \to [] + C \vdashinstr \TABLEINIT~x~y : [\X{at}~\I32~\I32] \to [] } @@ -1507,17 +1576,23 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* The offset :math:`\memarg.\OFFSET` must be less than :math:`2^{|\X{at}|}`. + * The alignment :math:`2^{\memarg.\ALIGN}` must not be larger than the :ref:`bit width ` of :math:`t` divided by :math:`8`. -* Then the instruction is valid with type :math:`[\I32] \to [t]`. +* Then the instruction is valid with type :math:`[\X{at}] \to [t]`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits + \qquad + \memarg.\OFFSET < 2^{|\X{at}|} \qquad 2^{\memarg.\ALIGN} \leq |t|/8 }{ - C \vdashinstr t\K{.load}~x~\memarg : [\I32] \to [t] + C \vdashinstr t\K{.load}~x~\memarg : [\X{at}] \to [t] } @@ -1528,17 +1603,23 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* The offset :math:`\memarg.\OFFSET` must be less than :math:`2^{|\X{at}|}`. + * The alignment :math:`2^{\memarg.\ALIGN}` must not be larger than :math:`N/8`. -* Then the instruction is valid with type :math:`[\I32] \to [t]`. +* Then the instruction is valid with type :math:`[\X{at}] \to [t]`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits + \qquad + \memarg.\OFFSET < 2^{|\X{at}|} \qquad 2^{\memarg.\ALIGN} \leq N/8 }{ - C \vdashinstr t\K{.load}N\K{\_}\sx~x~\memarg : [\I32] \to [t] + C \vdashinstr t\K{.load}N\K{\_}\sx~x~\memarg : [\X{at}] \to [t] } @@ -1547,17 +1628,23 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* The offset :math:`\memarg.\OFFSET` must be less than :math:`2^{|\X{at}|}`. + * The alignment :math:`2^{\memarg.\ALIGN}` must not be larger than the :ref:`bit width ` of :math:`t` divided by :math:`8`. -* Then the instruction is valid with type :math:`[\I32~t] \to []`. +* Then the instruction is valid with type :math:`[\X{at}~t] \to []`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits + \qquad + \memarg.\OFFSET < 2^{|\X{at}|} \qquad 2^{\memarg.\ALIGN} \leq |t|/8 }{ - C \vdashinstr t\K{.store}~x~\memarg : [\I32~t] \to [] + C \vdashinstr t\K{.store}~x~\memarg : [\X{at}~t] \to [] } @@ -1568,17 +1655,23 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* The offset :math:`\memarg.\OFFSET` must be less than :math:`2^{|\X{at}|}`. + * The alignment :math:`2^{\memarg.\ALIGN}` must not be larger than :math:`N/8`. -* Then the instruction is valid with type :math:`[\I32~t] \to []`. +* Then the instruction is valid with type :math:`[\X{at}~t] \to []`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits + \qquad + \memarg.\OFFSET < 2^{|\X{at}|} \qquad 2^{\memarg.\ALIGN} \leq N/8 }{ - C \vdashinstr t\K{.store}N~x~\memarg : [\I32~t] \to [] + C \vdashinstr t\K{.store}N~x~\memarg : [\X{at}~t] \to [] } @@ -1589,17 +1682,23 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* The offset :math:`\memarg.\OFFSET` must be less than :math:`2^{|\X{at}|}`. + * The alignment :math:`2^{\memarg.\ALIGN}` must not be larger than :math:`N/8 \cdot M`. -* Then the instruction is valid with type :math:`[\I32] \to [\V128]`. +* Then the instruction is valid with type :math:`[\X{at}] \to [\V128]`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits + \qquad + \memarg.\OFFSET < 2^{|\X{at}|} \qquad 2^{\memarg.\ALIGN} \leq N/8 \cdot M }{ - C \vdashinstr \K{v128.}\LOAD{N}\K{x}M\_\sx~x~\memarg : [\I32] \to [\V128] + C \vdashinstr \K{v128.}\LOAD{N}\K{x}M\_\sx~x~\memarg : [\X{at}] \to [\V128] } @@ -1610,17 +1709,23 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* The offset :math:`\memarg.\OFFSET` must be less than :math:`2^{|\X{at}|}`. + * The alignment :math:`2^{\memarg.\ALIGN}` must not be larger than :math:`N/8`. -* Then the instruction is valid with type :math:`[\I32] \to [\V128]`. +* Then the instruction is valid with type :math:`[\X{at}] \to [\V128]`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits + \qquad + \memarg.\OFFSET < 2^{|\X{at}|} \qquad 2^{\memarg.\ALIGN} \leq N/8 }{ - C \vdashinstr \K{v128.}\LOAD{N}\K{\_splat}~x~\memarg : [\I32] \to [\V128] + C \vdashinstr \K{v128.}\LOAD{N}\K{\_splat}~x~\memarg : [\X{at}] \to [\V128] } @@ -1631,17 +1736,23 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* The offset :math:`\memarg.\OFFSET` must be less than :math:`2^{|\X{at}|}`. + * The alignment :math:`2^{\memarg.\ALIGN}` must not be larger than :math:`N/8`. -* Then the instruction is valid with type :math:`[\I32] \to [\V128]`. +* Then the instruction is valid with type :math:`[\X{at}] \to [\V128]`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits + \qquad + \memarg.\OFFSET < 2^{|\X{at}|} \qquad 2^{\memarg.\ALIGN} \leq N/8 }{ - C \vdashinstr \K{v128.}\LOAD{N}\K{\_zero}~x~\memarg : [\I32] \to [\V128] + C \vdashinstr \K{v128.}\LOAD{N}\K{\_zero}~x~\memarg : [\X{at}] \to [\V128] } @@ -1652,21 +1763,27 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* The offset :math:`\memarg.\OFFSET` must be less than :math:`2^{|\X{at}|}`. + * The alignment :math:`2^{\memarg.\ALIGN}` must not be larger than :math:`N/8`. * The lane index :math:`\laneidx` must be smaller than :math:`128/N`. -* Then the instruction is valid with type :math:`[\I32~\V128] \to [\V128]`. +* Then the instruction is valid with type :math:`[\X{at}~\V128] \to [\V128]`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits \qquad - 2^{\memarg.\ALIGN} < N/8 + \memarg.\OFFSET < 2^{|\X{at}|} + \qquad + 2^{\memarg.\ALIGN} \leq N/8 \qquad \laneidx < 128/N }{ - C \vdashinstr \K{v128.}\LOAD{N}\K{\_lane}~x~\memarg~\laneidx : [\I32~\V128] \to [\V128] + C \vdashinstr \K{v128.}\LOAD{N}\K{\_lane}~x~\memarg~\laneidx : [\X{at}~\V128] \to [\V128] } @@ -1677,21 +1794,27 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* The offset :math:`\memarg.\OFFSET` must be less than :math:`2^{|\X{at}|}`. + * The alignment :math:`2^{\memarg.\ALIGN}` must not be larger than :math:`N/8`. * The lane index :math:`\laneidx` must be smaller than :math:`128/N`. -* Then the instruction is valid with type :math:`[\I32~\V128] \to [\V128]`. +* Then the instruction is valid with type :math:`[\X{at}~\V128] \to [\V128]`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits + \qquad + \memarg.\OFFSET < 2^{|\X{at}|} \qquad - 2^{\memarg.\ALIGN} < N/8 + 2^{\memarg.\ALIGN} \leq N/8 \qquad \laneidx < 128/N }{ - C \vdashinstr \K{v128.}\STORE{N}\K{\_lane}~x~\memarg~\laneidx : [\I32~\V128] \to [] + C \vdashinstr \K{v128.}\STORE{N}\K{\_lane}~x~\memarg~\laneidx : [\X{at}~\V128] \to [] } @@ -1702,13 +1825,15 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. -* Then the instruction is valid with type :math:`[] \to [\I32]`. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* Then the instruction is valid with type :math:`[] \to [\X{at}]`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits }{ - C \vdashinstr \MEMORYSIZE~x : [] \to [\I32] + C \vdashinstr \MEMORYSIZE~x : [] \to [\X{at}] } @@ -1719,13 +1844,15 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. -* Then the instruction is valid with type :math:`[\I32] \to [\I32]`. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* Then the instruction is valid with type :math:`[\X{at}] \to [\X{at}]`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits }{ - C \vdashinstr \MEMORYGROW~x : [\I32] \to [\I32] + C \vdashinstr \MEMORYGROW~x : [\X{at}] \to [\X{at}] } @@ -1736,13 +1863,15 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. -* Then the instruction is valid with type :math:`[\I32~\I32~\I32] \to []`. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* Then the instruction is valid with type :math:`[\X{at}~\I32~\X{at}] \to []`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits }{ - C \vdashinstr \MEMORYFILL~x : [\I32~\I32~\I32] \to [] + C \vdashinstr \MEMORYFILL~x : [\X{at}~\I32~\X{at}] \to [] } @@ -1755,15 +1884,21 @@ Memory Instructions * The memory :math:`C.\CMEMS[y]` must be defined in the context. -* Then the instruction is valid with type :math:`[\I32~\I32~\I32] \to []`. +* Let :math:`\X{at}_x~\limits_x` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + +* Let :math:`\X{at}_y~\limits_y` be the :ref:`memory type ` :math:`C.\CMEMS[y]`. + +* Let :math:`\X{at}` be the :ref:`minimum ` of :math:`\X{at}_x` and :math:`\X{at}_y` + +* Then the instruction is valid with type :math:`[\X{at}_x~\X{at}_y~\X{at}] \to []`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}_x~\limits_y \qquad - C.\CMEMS[x] = \memtype + C.\CMEMS[y] = \X{at}_y~\limits_y }{ - C \vdashinstr \MEMORYCOPY~x~y : [\I32~\I32~\I32] \to [] + C \vdashinstr \MEMORYCOPY~x~y : [\X{at}_x~\X{at}_y~\addrtypemin(\X{at}_x, \X{at}_y)] \to [] } @@ -1774,17 +1909,19 @@ Memory Instructions * The memory :math:`C.\CMEMS[x]` must be defined in the context. +* Let :math:`\X{at}~\limits` be the :ref:`memory type ` :math:`C.\CMEMS[x]`. + * The data segment :math:`C.\CDATAS[y]` must be defined in the context. -* Then the instruction is valid with type :math:`[\I32~\I32~\I32] \to []`. +* Then the instruction is valid with type :math:`[\X{at}~\I32~\I32] \to []`. .. math:: \frac{ - C.\CMEMS[x] = \memtype + C.\CMEMS[x] = \X{at}~\limits \qquad C.\CDATAS[y] = {\ok} }{ - C \vdashinstr \MEMORYINIT~x~y : [\I32~\I32~\I32] \to [] + C \vdashinstr \MEMORYINIT~x~y : [\X{at}~\I32~\I32] \to [] } @@ -1974,7 +2111,7 @@ Control Instructions * The tag :math:`C.\CTAGS[x]` must be defined in the context. -* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`tag type ` :math:`C.\CTAGS[x]`. +* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`expansion ` of the :ref:`tag type ` :math:`C.\CTAGS[x]`. * The :ref:`result type ` :math:`[{t'}^\ast]` must be empty. @@ -1986,7 +2123,7 @@ Control Instructions .. math:: \frac{ - C.\CTAGS[x] = [t^\ast] \toF [] + \expanddt(C.\CTAGS[x]) = [t^\ast] \toF [] \qquad C.\CLABELS[l] = [t^\ast] }{ @@ -1998,21 +2135,21 @@ Control Instructions * The tag :math:`C.\CTAGS[x]` must be defined in the context. -* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`tag type ` :math:`C.\CTAGS[x]`. +* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`expansion ` of the :ref:`tag type ` :math:`C.\CTAGS[x]`. * The :ref:`result type ` :math:`[{t'}^\ast]` must be empty. * The label :math:`C.\CLABELS[l]` must be defined in the context. -* The :ref:`result type ` :math:`[t^\ast]` must be the same as :math:`C.\CLABELS[l]` with |EXNREF| appended. +* The :ref:`result type ` :math:`[t^\ast~(\REF~\EXN)]` must :ref:`match ` :math:`C.\CLABELS[l]`. * Then the catch clause is valid. .. math:: \frac{ - C.\CTAGS[x] = [t^\ast] \toF [] + \expanddt(C.\CTAGS[x]) = [t^\ast] \toF [] \qquad - C.\CLABELS[l] = [t^\ast~\EXNREF] + C \vdashresulttypematch [t^\ast~(\REF~\EXN)] \matchesresulttype C.\CLABELS[l] }{ C \vdashcatch \CATCHREF~x~l \ok } @@ -2038,13 +2175,13 @@ Control Instructions * The label :math:`C.\CLABELS[l]` must be defined in the context. -* The :ref:`result type ` :math:`C.\CLABELS[l] must be :math:`[\EXNREF]`. +* The :ref:`result type ` :math:`[(\REF~\EXN)]` must :ref:`match ` :math:`C.\CLABELS[l]`. * Then the catch clause is valid. .. math:: \frac{ - C.\CLABELS[l] = [\EXNREF] + C \vdashresulttypematch [(\REF~\EXN)] \matchesresulttype C.\CLABELS[l] }{ C \vdashcatch \CATCHALLREF~l \ok } @@ -2273,7 +2410,7 @@ Control Instructions * Let :math:`[t^\ast]` be the :ref:`result type ` of :math:`C.\CRETURN`. -* Then the instruction is valid with any :ref:`valid ` type of the form :math:`[t_1^\ast] \to [t_2^\ast]`. +* Then the instruction is valid with any :ref:`valid ` type of the form :math:`[t_1^\ast~t^\ast] \to [t_2^\ast]`. .. math:: \frac{ @@ -2337,7 +2474,7 @@ Control Instructions * The table :math:`C.\CTABLES[x]` must be defined in the context. -* Let :math:`\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. +* Let :math:`\X{at}~\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. * The :ref:`reference type ` :math:`t` must :ref:`match ` type :math:`\REF~\NULL~\FUNC`. @@ -2345,17 +2482,17 @@ Control Instructions * The :ref:`expansion ` of :math:`C.\CTYPES[y]` must be a :ref:`function type ` :math:`\TFUNC~[t_1^\ast] \toF [t_2^\ast]`. -* Then the instruction is valid with type :math:`[t_1^\ast~\I32] \to [t_2^\ast]`. +* Then the instruction is valid with type :math:`[t_1^\ast~\X{at}] \to [t_2^\ast]`. .. math:: \frac{ - C.\CTABLES[x] = \limits~t + C.\CTABLES[x] = \X{at}~\limits~t \qquad C \vdashvaltypematch t \matchesreftype \REF~\NULL~\FUNC \qquad \expanddt(C.\CTYPES[y]) = \TFUNC~[t_1^\ast] \toF [t_2^\ast] }{ - C \vdashinstr \CALLINDIRECT~x~y : [t_1^\ast~\I32] \to [t_2^\ast] + C \vdashinstr \CALLINDIRECT~x~y : [t_1^\ast~\X{at}] \to [t_2^\ast] } @@ -2426,7 +2563,7 @@ Control Instructions * The table :math:`C.\CTABLES[x]` must be defined in the context. -* Let :math:`\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. +* Let :math:`\X{at}~\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. * The :ref:`reference type ` :math:`t` must :ref:`match ` type :math:`\REF~\NULL~\FUNC`. @@ -2436,11 +2573,11 @@ Control Instructions * The :ref:`result type ` :math:`[t_2^\ast]` must :ref:`match ` :math:`C.\CRETURN`. -* Then the instruction is valid with type :math:`[t_3^\ast~t_1^\ast~\I32] \to [t_4^\ast]`, for any sequences of :ref:`value types ` :math:`t_3^\ast` and :math:`t_4^\ast`. +* Then the instruction is valid with type :math:`[t_3^\ast~t_1^\ast~\X{at}] \to [t_4^\ast]`, for any sequences of :ref:`value types ` :math:`t_3^\ast` and :math:`t_4^\ast`. .. math:: \frac{ - C.\CTABLES[x] = \limits~t + C.\CTABLES[x] = \X{at}~\limits~t \qquad C \vdashvaltypematch t \matchesreftype \REF~\NULL~\FUNC \qquad @@ -2450,7 +2587,7 @@ Control Instructions \qquad C \vdashinstrtype [t_3^\ast~t_1^\ast~\I32] \to [t_4^\ast] \ok }{ - C \vdashinstr \RETURNCALLINDIRECT~x~y : [t_3^\ast~t_1^\ast~\I32] \to [t_4^\ast] + C \vdashinstr \RETURNCALLINDIRECT~x~y : [t_3^\ast~t_1^\ast~\X{at}] \to [t_4^\ast] } .. note:: @@ -2464,15 +2601,15 @@ Control Instructions * The tag :math:`C.\CTAGS[x]` must be defined in the context. -* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`tag type ` :math:`C.\CTAGS[x]`. +* Let :math:`[t^\ast] \to [{t'}^\ast]` be the :ref:`expansion ` of the :ref:`tag type ` :math:`C.\CTAGS[x]`. * The :ref:`result type ` :math:`[{t'}^\ast]` must be empty. -* Then the instruction is valid with type :math:`[t_1^\ast t^\ast] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. +* Then the instruction is valid with type :math:`[t_1^\ast t^\ast] \to [t_2^\ast]`, for any sequences of :ref:`value types ` :math:`t_1^\ast` and :math:`t_2^\ast`. .. math:: \frac{ - C.\CTAGS[x] = [t^\ast] \to [] + \expanddt(C.\CTAGS[x]) = [t^\ast] \to [] }{ C \vdashinstr \THROW~x : [t_1^\ast~t^\ast] \to [t_2^\ast] } diff --git a/document/core/valid/matching.rst b/document/core/valid/matching.rst index e5575b5ad..440e3d2b9 100644 --- a/document/core/valid/matching.rst +++ b/document/core/valid/matching.rst @@ -456,7 +456,7 @@ A :ref:`defined type ` :math:`\deftype_1` matches a type :math:` .. note:: Note that there is no explicit definition of type _equivalence_, since it coincides with syntactic equality, - as used in the premise of the fomer rule above. + as used in the premise of the former rule above. .. index:: limits @@ -502,7 +502,9 @@ Limits Table Types ~~~~~~~~~~~ -A :ref:`table type ` :math:`(\limits_1~\reftype_1)` matches :math:`(\limits_2~\reftype_2)` if and only if: +A :ref:`table type ` :math:`(\addrtype_1~\limits_1~\reftype_1)` matches :math:`(\addrtype_2~\limits_2~\reftype_2)` if and only if: + +* Address types :math:`\addrtype_1` and :math:`\addrtype_2` are the same. * Limits :math:`\limits_1` :ref:`match ` :math:`\limits_2`. @@ -517,7 +519,7 @@ A :ref:`table type ` :math:`(\limits_1~\reftype_1)` matches :m \qquad C \vdashreftypematch \reftype_2 \matchesreftype \reftype_1 }{ - C \vdashtabletypematch \limits_1~\reftype_1 \matchestabletype \limits_2~\reftype_2 + C \vdashtabletypematch \addrtype~\limits_1~\reftype_1 \matchestabletype \addrtype~\limits_2~\reftype_2 } @@ -527,7 +529,9 @@ A :ref:`table type ` :math:`(\limits_1~\reftype_1)` matches :m Memory Types ~~~~~~~~~~~~ -A :ref:`memory type ` :math:`\limits_1` matches :math:`\limits_2` if and only if: +A :ref:`memory type ` :math:`(\addrtype_1~\limits_1)` matches :math:`(\addrtype_2~\limits_2)` if and only if: + +* Address types :math:`\addrtype_1` and :math:`\addrtype_2` are the same. * Limits :math:`\limits_1` :ref:`match ` :math:`\limits_2`. @@ -537,7 +541,7 @@ A :ref:`memory type ` :math:`\limits_1` matches :math:`\limits_2 \frac{ C \vdashlimitsmatch \limits_1 \matcheslimits \limits_2 }{ - C \vdashmemtypematch \limits_1 \matchesmemtype \limits_2 + C \vdashmemtypematch \addrtype~\limits_1 \matchesmemtype \addrtype~\limits_2 } @@ -576,14 +580,22 @@ A :ref:`global type ` :math:`(\mut_1~t_1)` matches :math:`(\m Tag Types ~~~~~~~~~ -A :ref:`tag type ` :math:`\tagtype_1` matches :math:`\tagtype_2` if and only if they are the same. +A :ref:`tag type ` :math:`\deftype_1` matches :math:`\deftype_2` if and only if the :ref:`defined type ` :math:`\deftype_1` :ref:`matches ` :math:`\deftype_2`, and vice versa. .. math:: \frac{ + C \vdashdeftypematch \deftype_1 \matchesdeftype \deftype_2 + \qquad + C \vdashdeftypematch \deftype_2 \matchesdeftype \deftype_1 }{ - C \vdashtagtypematch \tagtype \matchestagtype \tagtype + C \vdashtagtypematch \deftype_1 \matchestagtype \deftype_2 } +.. note:: + Although the conclusion of this rule looks identical to its premise, + they in fact describe different relations: + the premise invokes subtyping on defined types, + while the conclusion defines it on tag types that happen to be expressed as defined types. .. index:: external type, function type, table type, memory type, global type .. _match-externtype: diff --git a/document/core/valid/modules.rst b/document/core/valid/modules.rst index 04388145a..e110d0b6f 100644 --- a/document/core/valid/modules.rst +++ b/document/core/valid/modules.rst @@ -841,8 +841,6 @@ The :ref:`external types ` classifying a module may contain f * Let :math:`\X{et}^\ast` be the concatenation of :ref:`external types ` :math:`\X{et}_i` of the exports, in index order. -* The length of :math:`C.\CMEMS` must not be larger than :math:`1`. - * All export names :math:`\export_i.\ENAME` must be different. * Then the module is valid with :ref:`external types ` :math:`\X{it}^\ast \to \X{et}^\ast`. diff --git a/document/core/valid/types.rst b/document/core/valid/types.rst index e6ab987b3..98e54959c 100644 --- a/document/core/valid/types.rst +++ b/document/core/valid/types.rst @@ -51,7 +51,8 @@ Vector Types Heap Types ~~~~~~~~~~ -Concrete :ref:`Heap types ` are only valid when the :ref:`type index ` is. +Concrete :ref:`heap types ` are only valid when the :ref:`type index ` is, +while abstract ones are vacuously valid. :math:`\absheaptype` .................... @@ -500,10 +501,10 @@ Limits Table Types ~~~~~~~~~~~ -:math:`\limits~\reftype` -........................ +:math:`\addrtype~\limits~\reftype` +.................................. -* The limits :math:`\limits` must be :ref:`valid ` within range :math:`2^{32}-1`. +* The limits :math:`\limits` must be :ref:`valid ` within range :math:`2^{|\addrtype|}-1`. * The reference type :math:`\reftype` must be :ref:`valid `. @@ -511,11 +512,11 @@ Table Types .. math:: \frac{ - C \vdashlimits \limits : 2^{32} - 1 + C \vdashlimits \limits : 2^{|\addrtype|}-1 \qquad C \vdashreftype \reftype \ok }{ - C \vdashtabletype \limits~\reftype \ok + C \vdashtabletype \addrtype~\limits~\reftype \ok } @@ -527,16 +528,16 @@ Table Types Memory Types ~~~~~~~~~~~~ -:math:`\limits` -............... +:math:`\addrtype~\limits` +......................... -* The limits :math:`\limits` must be :ref:`valid ` within range :math:`2^{16}`. +* The limits :math:`\limits` must be :ref:`valid ` within range :math:`2^{|\addrtype|-16}`. * Then the memory type is valid. .. math:: \frac{ - C \vdashlimits \limits : 2^{16} + C \vdashlimits \limits : 2^{|\addrtype|-16} }{ C \vdashmemtype \limits \ok } @@ -550,10 +551,13 @@ Memory Types Tag Types ~~~~~~~~~ -:math:`[t_1^n] \to [t_2^m]` -........................... +:math:`\deftype` +................ -* The :ref:`function type ` :math:`[t_1^n] \to [t_2^m]` must be :ref:`valid `. + +* The :ref:`defined type ` :math:`\deftype` must be :ref:`valid `. + +* The :ref:`expansion ` of :math:`\deftype` must be a :ref:`function type ` :math:`\TFUNC~[t_1^n] \toF [t_2^m]`. * The type sequence :math:`t_2^m` must be empty. @@ -561,8 +565,11 @@ Tag Types .. math:: \frac{ + C \vdashdeftype \deftype \ok + \qquad + \expanddt(\deftype) = \TFUNC~[t^\ast] \to [] }{ - \vdashtagtype [t^\ast] \to [] \ok + C \vdashtagtype \deftype \ok } diff --git a/document/index.html b/document/index.html index a5766fac4..c78ff112c 100644 --- a/document/index.html +++ b/document/index.html @@ -14,13 +14,20 @@
+

WebAssembly Specifications

To support the embedding of WebAssembly into different environments, its specification is split into layers that are specified in separate documents.

-

Core specification

+

+ Source for these documents is available + here. +

+ + +

Core Specification

Defines the semantics of WebAssembly modules independent from a concrete embedding. The WebAssembly core is specified in a single document:

@@ -41,12 +48,13 @@

Core specification

-

Embedder specifications

+ +

Embedder Specifications

Define application programming interfaces (APIs) enabling the use of WebAssembly modules in concrete embedding environments. Currently, two APIs are specified:

-
    +
    • JavaScript Embedding: defines JavaScript classes and objects for accessing WebAssembly from within JavaScript, including methods for validation, compilation, instantiation, and classes for representing and manipulating imports and exports as JavaScript objects.

      • W3C version
      • @@ -60,6 +68,20 @@

        Embedder specifications

    • +
    + + +

    Metadata specifications

    + +

    Define the format and semantics of extra information attached to a WebAssembly module

    + +

    Legacy Extensions

    @@ -69,17 +91,29 @@

    Legacy Extensions

    -

    - Source for these documents is available - here. -

    +

    All Versions

    + +
diff --git a/document/js-api/Makefile b/document/js-api/Makefile index fcc69f7b3..acd14cf25 100644 --- a/document/js-api/Makefile +++ b/document/js-api/Makefile @@ -1,14 +1,17 @@ +W3C_STATUS = ED BUILDDIR = _build STATICDIR = _static DOWNLOADDIR = _download NAME = WebAssembly -DECISION_URL = https://github.com/WebAssembly/meetings/blob/main/main/2017/WG-12-06.md +DECISION_URL = https://github.com/WebAssembly/meetings/blob/main/main/2024/WG-06-12.md TAR = tar +DEADLINE = $(shell date -d "+30 days" +%Y-%m-%d 2>/dev/null || date -v +30d +%Y-%m-%d) +ECHIDNA_DRYRUN = true .PHONY: all all: mkdir -p $(BUILDDIR)/html - bikeshed spec index.bs $(BUILDDIR)/html/index.html + bikeshed spec --md-status=$(W3C_STATUS) --md-deadline=$(DEADLINE) index.bs $(BUILDDIR)/html/index.html @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html." .PHONY: publish @@ -33,7 +36,6 @@ diff: all # macOS tar has no “--transform” option (only GNU tar does), so on macOS, # do “brew install tar” & run “make” like this: “TAR=gtar make -e WD-tar” WD-tar: all - bikeshed spec --md-status=WD index.bs $(BUILDDIR)/html/index.html $(TAR) -C $(BUILDDIR)/html --transform="s/index.html/Overview.html/" -cf $(BUILDDIR)/WD.tar index.html @echo "Built $(BUILDDIR)/WD.tar." @@ -48,7 +50,11 @@ WD-echidna: WD-tar curl 'https://labs.w3.org/echidna/api/request' \ --user '$(W3C_USERNAME):$(W3C_PASSWORD)' \ -F "tar=@$(BUILDDIR)/WD.tar" \ - -F "decision=$(DECISION_URL)" | tee $(BUILDDIR)/WD-echidna-id.txt + -F "decision=$(DECISION_URL)" \ + -F "dry-run=$(ECHIDNA_DRYRUN)" | tee $(BUILDDIR)/WD-echidna-id.txt + python3 ../util/check-echidna-status.py $(BUILDDIR) + @echo + @echo "Uploaded $(W3C_STATUS). Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" .PHONY: WD-echidna-CI WD-echidna-CI: WD-tar @@ -60,6 +66,8 @@ WD-echidna-CI: WD-tar curl 'https://labs.w3.org/echidna/api/request' \ -F "tar=@$(BUILDDIR)/WD.tar" \ -F "token=$(W3C_ECHIDNA_TOKEN_JSAPI)" \ - -F "decision=$(DECISION_URL)" | tee $(BUILDDIR)/WD-echidna-id.txt + -F "decision=$(DECISION_URL)" \ + -F "dry-run=$(ECHIDNA_DRYRUN)" | tee $(BUILDDIR)/WD-echidna-id.txt + python3 ../util/check-echidna-status.py $(BUILDDIR) @echo - @echo "Published working draft. Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" + @echo "Uploaded $(W3C_STATUS). Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 5ecdd3e50..6295ec146 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -7,6 +7,7 @@ Issue Tracking: GitHub https://github.com/WebAssembly/spec/issues Level: 2 TR: https://www.w3.org/TR/wasm-js-api-2/ ED: https://webassembly.github.io/spec/js-api/ +Implementation Report: https://webassembly.org/features/ Editor: Ms2ger, w3cid 46309, Igalia Repository: WebAssembly/spec Markup Shorthands: css no, markdown yes @@ -22,6 +23,12 @@ Date: now "title": "WebAssembly Core Specification", "publisher": "W3C WebAssembly Community Group", "status": "Draft" + }, + "WASMWEB": { + "href": "https://webassembly.github.io/spec/js-api/", + "title": "WebAssembly Web API Specification", + "publisher": "W3C WebAssembly Community Group", + "status": "Draft" } }
@@ -47,6 +54,7 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT text: SetFunctionLength; url: sec-setfunctionlength text: the Number value; url: sec-ecmascript-language-types-number-type text: is a Number; url: sec-ecmascript-language-types-number-type + text: is a BigInt; url: sec-ecmascript-language-types-bigint-type text: NumberToRawBytes; url: sec-numbertorawbytes text: Built-in Function Objects; url: sec-built-in-function-objects text: NativeError Object Structure; url: sec-nativeerror-object-structure @@ -61,13 +69,19 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT text: NativeError Object Structure; url: sec-nativeerror-object-structure text: 𝔽; url: #𝔽 text: ℤ; url: #ℤ + text: mathematical value; url: #mathematical-value text: SameValue; url: sec-samevalue - type: abstract-op - text: CreateMethodProperty; url: sec-createmethodproperty + text: Array; url: sec-array-exotic-objects + text: BigInt; url: sec-ecmascript-language-types-bigint-type urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: dfn + text: embedding interface; url: appending/embedding.html + text: scope; url: intro/introduction.html#scope url: valid/modules.html#valid-module text: valid text: WebAssembly module validation + text: valid limits; url: valid/types.html#valid-limits + text: valid memtype; url: valid/types.html#valid-memtype + text: valid tabletype; url: valid/types.html#valid-tabletype text: module grammar; url: binary/modules.html#binary-module text: custom section; url: binary/modules.html#custom-section text: customsec; url: binary/modules.html#binary-customsec @@ -89,6 +103,7 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df text: ref.func text: ref.host text: ref.extern + text: ref.exn text: function index; url: syntax/modules.html#syntax-funcidx text: function instance; url: exec/runtime.html#function-instances text: store_init; url: appendix/embedding.html#embed-store-init @@ -118,20 +133,34 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df text: global_read; url: appendix/embedding.html#embed-global-read text: global_write; url: appendix/embedding.html#embed-global-write text: ref_type; url: appendix/embedding.html#embed-ref-type + text: val_default; url: appendix/embedding.html#embed-val-default text: match_valtype; url: appendix/embedding.html#embed-match-valtype text: error; url: appendix/embedding.html#embed-error text: store; url: exec/runtime.html#syntax-store + text: address type; url: syntax/types.html#syntax-addrtype + text: limits; url: syntax/types.html#syntax-limits text: table type; url: syntax/types.html#syntax-tabletype text: table address; url: exec/runtime.html#syntax-tableaddr text: function address; url: exec/runtime.html#syntax-funcaddr + text: memory type; url: syntax/types.html#syntax-memtype text: memory address; url: exec/runtime.html#syntax-memaddr text: global address; url: exec/runtime.html#syntax-globaladdr text: tag address; url: exec/runtime.html#syntax-tagaddr + text: tag type; url: syntax/types.html#syntax-tagtype text: struct address; url: exec/runtime.html#syntax-structaddr text: array address; url: exec/runtime.html#syntax-arrayaddr + text: exception address; url: exec/runtime.html#syntax-exnaddr text: host address; url: exec/runtime.html#syntax-hostaddr text: extern address; url: exec/runtime.html#syntax-externaddr text: page size; url: exec/runtime.html#page-size + text: tag_alloc; url: appendix/embedding.html#embed-tag-alloc + text: tag_type; url: appendix/embedding.html#embed-tag-type + text: exn_alloc; url: appendix/embedding.html#embed-exn-alloc + text: exn_tag; url: appendix/embedding.html#embed-exn-tag + text: exn_read; url: appendix/embedding.html#embed-exn-read + url: syntax/values.html#syntax-int + text: u32 + text: u64 url: syntax/types.html#syntax-numtype text: i32 text: i64 @@ -183,6 +212,7 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df text: signed_31; url: exec/numerics.html#aux-signed text: signed_32; url: exec/numerics.html#aux-signed text: memory.grow; url: exec/instructions.html#exec-memory-grow + text: throw_ref; url: exec/instructions.html#exec-throw-ref text: current frame; url: exec/conventions.html#exec-notation-textual text: module; for: frame; url: exec/runtime.html#syntax-frame text: memaddrs; for: moduleinst; url: exec/runtime.html#syntax-moduleinst @@ -192,6 +222,9 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df urlPrefix: https://heycam.github.io/webidl/; spec: WebIDL type: dfn text: create a namespace object; url: create-a-namespace-object + text: [EnforceRange]; url: #EnforceRange + text: unsigned long; url: #idl-unsigned-long + text: js-unsigned-long; url: #js-unsigned-long urlPrefix: https://webassembly.github.io/js-types/js-api/; spec: WebAssembly JS API (JS Type Reflection) type: abstract-op; text: FromValueType; url: abstract-opdef-fromvaluetype urlPrefix: https://tc39.es/proposal-resizablearraybuffer/; spec: ResizableArrayBuffer proposal @@ -217,7 +250,14 @@ emu-const { } -This API provides a way to access WebAssembly [[WEBASSEMBLY]] through a bridge to explicitly construct modules from JavaScript [[ECMASCRIPT]]. +

Introduction

+ +By design, the [=scope=] of the WebAssembly core specification [[WEBASSEMBLY]] does not include a description of how WebAssembly programs interact with their surrounding execution environment. +Instead it defines an abstract [=embedding interface=] between WebAssembly and its environment, (called the *embedder*). +It is only through this interface that an embedder interacts with the semantics of WebAssembly, and the embedder implements the connection between its host environment and the embedding API. +This document describes the embedding of WebAssembly into JavaScript [[ECMASCRIPT]] environments, including how WebAssembly modules can be constructed and instantiated, how imported and exported functions are called, how data is exchanged, and how errors are handled. +When the JavaScript environment is itself embedded in a Web browser, the Web API spec [[WASMWEB]] describes additional behavior relevant to the Web environment. +

Sample API Usage

@@ -282,8 +322,10 @@ Each [=agent=] is associated with the following [=ordered map=]s: * The Exported GC Object cache, mapping [=struct address=]es and [=array address=]es to [=Exported GC Object=] objects. * The Global object cache, mapping [=global address=]es to {{Global}} objects. * The Tag object cache, mapping [=tag addresses=] to {{Tag}} objects. + * The Exception object cache, mapping [=exception address=]es to {{Exception}} objects. * The Host value cache, mapping [=host address=]es to values. +

The WebAssembly Namespace

@@ -302,6 +344,8 @@ namespace WebAssembly {
 
     Promise<Instance> instantiate(
         Module moduleObject, optional object importObject);
+
+    readonly attribute Tag JSTag;
 };
 
@@ -381,7 +425,7 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje 1. Let |index| be the number of external functions in |imports|. This value |index| is known as the index of the host function |funcaddr|. 1. Let |externfunc| be the [=external value=] [=external value|func=] |funcaddr|. 1. [=list/Append=] |externfunc| to |imports|. - 1. If |externtype| is of the form [=global=] mut |valtype|, + 1. If |externtype| is of the form [=external-type/global=] mut |valtype|, 1. If |v| [=implements=] {{Global}}, 1. Let |globaladdr| be |v|.\[[Global]]. 1. Otherwise, @@ -391,22 +435,22 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje 1. Throw a {{LinkError}} exception. 1. If |valtype| is [=v128=], 1. Throw a {{LinkError}} exception. - 1. Let |value| be [=ToWebAssemblyValue=](|v|, |valtype|). + 1. Let |value| be [=ToWebAssemblyValue=](|v|, |valtype|). If this operation throws a {{TypeError}}, catch it, and throw a {{LinkError}} exception. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, [=const=] |valtype|, |value|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. Let |externglobal| be [=external value|global=] |globaladdr|. 1. [=list/Append=] |externglobal| to |imports|. - 1. If |externtype| is of the form [=mem=] memtype, + 1. If |externtype| is of the form [=external-type/mem=] memtype, 1. If |v| does not [=implement=] {{Memory}}, throw a {{LinkError}} exception. 1. Let |externmem| be the [=external value=] [=external value|mem=] |v|.\[[Memory]]. 1. [=list/Append=] |externmem| to |imports|. - 1. If |externtype| is of the form [=table=] tabletype, + 1. If |externtype| is of the form [=external-type/table=] tabletype, 1. If |v| does not [=implement=] {{Table}}, throw a {{LinkError}} exception. 1. Let |tableaddr| be |v|.\[[Table]]. 1. Let |externtable| be the [=external value=] [=external value|table=] |tableaddr|. 1. [=list/Append=] |externtable| to |imports|. - 1. If |externtype| is of the form [=externtype/tag=] |attribute| functype, + 1. If |externtype| is of the form [=external-type/tag=] |attribute| functype, 1. Assert: |attribute| is [=tagtype/attribute/exception=]. 1. If |v| does not [=implement=] {{Tag}}, throw a {{LinkError}} exception. 1. Let |tagaddr| be |v|.\[[Address]]. @@ -430,22 +474,22 @@ The verification of WebAssembly type requirements is deferred to the 1. Let [=external value|func=] |funcaddr| be |externval|. 1. Let |func| be the result of creating [=a new Exported Function=] from |funcaddr|. 1. Let |value| be |func|. - 1. If |externtype| is of the form [=global=] mut globaltype, + 1. If |externtype| is of the form [=external-type/global=] mut globaltype, 1. Assert: |externval| is of the form [=external value|global=] |globaladdr|. 1. Let [=external value|global=] |globaladdr| be |externval|. 1. Let |global| be [=create a global object|a new Global object=] created from |globaladdr|. 1. Let |value| be |global|. - 1. If |externtype| is of the form [=mem=] memtype, + 1. If |externtype| is of the form [=external-type/mem=] memtype, 1. Assert: |externval| is of the form [=external value|mem=] |memaddr|. 1. Let [=external value|mem=] |memaddr| be |externval|. 1. Let |memory| be [=create a memory object|a new Memory object=] created from |memaddr|. 1. Let |value| be |memory|. - 1. If |externtype| is of the form [=table=] tabletype, + 1. If |externtype| is of the form [=external-type/table=] tabletype, 1. Assert: |externval| is of the form [=external value|table=] |tableaddr|. 1. Let [=external value|table=] |tableaddr| be |externval|. 1. Let |table| be [=create a Table object|a new Table object=] created from |tableaddr|. 1. Let |value| be |table|. - 1. If |externtype| is of the form [=externtype/tag=] |attribute| functype, + 1. If |externtype| is of the form [=external-type/tag=] |attribute| functype, 1. Assert: |attribute| is [=tagtype/attribute/exception=]. 1. Assert: |externval| is of the form [=external value/tag=] |tagaddr|. 1. Let [=external value/tag=] |tagaddr| be |externval|. @@ -528,6 +572,11 @@ The verification of WebAssembly type requirements is deferred to the Note: A follow-on streaming API is documented in the WebAssembly Web API. +The getter of the JSTag attribute of the {{WebAssembly}} Namespace, when invoked, performs the following steps: + 1. Let |JSTagAddr| be the result of [=get the JavaScript exception tag|getting the JavaScript exception tag=]. + 1. Let |JSTagObject| be the result of [=create a Tag object|creating a Tag object=] from |JSTagAddr|. + 1. Return |JSTagObject|. +

Modules

@@ -539,6 +588,13 @@ enum ImportExportKind {
   "tag"
 };
 
+enum AddressType {
+  "i32",
+  "i64",
+};
+
+typedef any AddressValue;
+
 dictionary ModuleExportDescriptor {
   required USVString name;
   required ImportExportKind kind;
@@ -563,10 +619,10 @@ interface Module {
 
The string value of the extern type |type| is * "function" if |type| is of the form [=external-type/func=] functype - * "table" if |type| is of the form [=table=] tabletype - * "memory" if |type| is of the form [=mem=] memtype - * "global" if |type| is of the form [=global=] globaltype - * "tag" if |type| is of the form [=externtype/tag=] tag + * "table" if |type| is of the form [=external-type/table=] tabletype + * "memory" if |type| is of the form [=external-type/mem=] memtype + * "global" if |type| is of the form [=external-type/global=] globaltype + * "tag" if |type| is of the form [=external-type/tag=] tag
@@ -643,14 +699,15 @@ Note: The use of this synchronous API is discouraged, as some implementations so
 dictionary MemoryDescriptor {
-  required [EnforceRange] unsigned long initial;
-  [EnforceRange] unsigned long maximum;
+  required AddressValue initial;
+  AddressValue maximum;
+  AddressType address;
 };
 
 [LegacyNamespace=WebAssembly, Exposed=*]
 interface Memory {
   constructor(MemoryDescriptor descriptor);
-  unsigned long grow([EnforceRange] unsigned long delta);
+  AddressValue grow(AddressValue delta);
   ArrayBuffer toFixedLengthBuffer();
   ArrayBuffer toResizableBuffer();
   readonly attribute ArrayBuffer buffer;
@@ -713,10 +770,11 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
 
 
The Memory(|descriptor|) constructor, when invoked, performs the following steps: - 1. Let |initial| be |descriptor|["initial"]. - 1. If |descriptor|["maximum"] [=map/exists=], let |maximum| be |descriptor|["maximum"]; otherwise, let |maximum| be empty. - 1. If |maximum| is not empty and |maximum| < |initial|, throw a {{RangeError}} exception. - 1. Let |memtype| be { min |initial|, max |maximum| }. + 1. If |descriptor|["address"] [=map/exists=], let |addrtype| be |descriptor|["address"]; otherwise, let |addrtype| be "i32". + 1. Let |initial| be [=?=] [=AddressValueToU64=](|descriptor|["initial"], |addrtype|). + 1. If |descriptor|["maximum"] [=map/exists=], let |maximum| be [=?=] [=AddressValueToU64=](|descriptor|["maximum"], |addrtype|); otherwise, let |maximum| be empty. + 1. Let |memtype| be [=memory type=] |addrtype| { **min** |initial|, **max** |maximum| }. + 1. If |memtype| is not [=valid memtype|valid=], throw a {{RangeError}} exception. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let (|store|, |memaddr|) be [=mem_alloc=](|store|, |memtype|). If allocation fails, throw a {{RangeError}} exception. 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. @@ -755,7 +813,11 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
The grow(|delta|) method, when invoked, performs the following steps: 1. Let |memaddr| be **this**.\[[Memory]]. - 1. Return the result of [=grow the memory buffer|growing the memory buffer=] associated with |memaddr| by |delta|. + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |addrtype| be the [=address type=] in [=mem_type=](|store|, |memaddr|). + 1. Let |delta64| be [=?=] [=AddressValueToU64=](|delta|, |addrtype|). + 1. Let |ret| be the result of [=grow the memory buffer|growing the memory buffer=] associated with |memaddr| by |delta64|. + 1. Return [=U64ToAddressValue=](|ret|, |addrtype|).
Immediately after a WebAssembly [=memory.grow=] instruction executes, perform the following steps: @@ -799,7 +861,8 @@ Immediately after a WebAssembly [=memory.grow=] instruction executes, perform th {{ArrayBuffer}} objects returned by a {{Memory}} object must have a size that is a multiple of a WebAssembly [=page size=] (the constant 65536). For this reason [=HostResizeArrayBuffer=] is redefined as follows.
- The abstract operation [=HostResizeArrayBuffer=] takes arguments |buffer| (an {{ArrayBuffer}}) and |newLength|. It performs the following steps when called. + + The abstract operation [=HostResizeArrayBuffer=] takes arguments |buffer| (an {{ArrayBuffer}}) and |newLength|. It performs the following steps when called. 1. If |buffer|.\[[ArrayBufferDetachKey]] is "WebAssembly.Memory", 1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=]. @@ -832,17 +895,18 @@ enum TableKind { dictionary TableDescriptor { required TableKind element; - required [EnforceRange] unsigned long initial; - [EnforceRange] unsigned long maximum; + required AddressValue initial; + AddressValue maximum; + AddressType address; }; [LegacyNamespace=WebAssembly, Exposed=*] interface Table { constructor(TableDescriptor descriptor, optional any value); - unsigned long grow([EnforceRange] unsigned long delta, optional any value); - any get([EnforceRange] unsigned long index); - undefined set([EnforceRange] unsigned long index, optional any value); - readonly attribute unsigned long length; + AddressValue grow(AddressValue delta, optional any value); + any get(AddressValue index); + undefined set(AddressValue index, optional any value); + readonly attribute AddressValue length; };
@@ -870,19 +934,21 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address
The Table(|descriptor|, |value|) constructor, when invoked, performs the following steps: - 1. Let |elementType| be [=ToValueType=](|descriptor|["element"]). - 1. If |elementType| is not a [=reftype=], + 1. Let |elementtype| be [=ToValueType=](|descriptor|["element"]). + 1. If |elementtype| is not a [=reftype=], 1. [=Throw=] a {{TypeError}} exception. - 1. Let |initial| be |descriptor|["initial"]. - 1. If |descriptor|["maximum"] [=map/exists=], let |maximum| be |descriptor|["maximum"]; otherwise, let |maximum| be empty. - 1. If |maximum| is not empty and |maximum| < |initial|, throw a {{RangeError}} exception. + 1. If |descriptor|["address"] [=map/exists=], let |addrtype| be |descriptor|["address"]; otherwise, let |addrtype| be "i32". + 1. Let |initial| be [=?=] [=AddressValueToU64=](|descriptor|["initial"], |addrtype|). + 1. If |descriptor|["maximum"] [=map/exists=], let |maximum| be [=?=] [=AddressValueToU64=](|descriptor|["maximum"], |addrtype|); otherwise, let |maximum| be empty. + 1. Let |type| be the [=table type=] |addrtype| { [=limits|min=] |initial|, [=limits|max=] |maximum| } |elementType|. + 1. If |type| is not [=valid tabletype|valid=], throw a {{RangeError}} exception. 1. If |value| is missing, - 1. Let |ref| be [=DefaultValue=](|elementType|). + 1. Let |ref| be [=DefaultValue=](|elementtype|). + 1. Assert: |ref| is not [=error=]. 1. Otherwise, 1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementType|). - 1. Let |type| be the [=table type=] {[=table type|min=] |initial|, [=table type|max=] |maximum|} |elementType|. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. - 1. Let (|store|, |tableaddr|) be [=table_alloc=](|store|, |type|, |ref|). + 1. Let (|store|, |tableaddr|) be [=table_alloc=](|store|, |type|, |ref|). If allocation fails, throw a {{RangeError}} exception. 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. [=initialize a table object|Initialize=] **this** from |tableaddr|.
@@ -892,12 +958,14 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address 1. Let |tableaddr| be **this**.\[[Table]]. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let |initialSize| be [=table_size=](|store|, |tableaddr|). - 1. Let (limits, |elementType|) be [=table_type=](|tableaddr|). + 1. Let (|addrtype|, limits, |elementtype|) be [=table_type=](|store|, |tableaddr|). + 1. Let |delta64| be [=?=] [=AddressValueToU64=](|delta|, |addrtype|). 1. If |value| is missing, - 1. Let |ref| be [=DefaultValue=](|elementType|). + 1. Let |ref| be [=DefaultValue=](|elementtype|). + 1. If |ref| is [=error=], throw a {{TypeError}} exception. 1. Otherwise, - 1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementType|). - 1. Let |result| be [=table_grow=](|store|, |tableaddr|, |delta|, |ref|). + 1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementtype|). + 1. Let |result| be [=table_grow=](|store|, |tableaddr|, |delta64|, |ref|). 1. If |result| is [=error=], throw a {{RangeError}} exception. Note: The above exception can happen due to either insufficient memory or an invalid size parameter. @@ -910,17 +978,20 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address The getter of the length attribute of {{Table}}, when invoked, performs the following steps: 1. Let |tableaddr| be **this**.\[[Table]]. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. - 1. Return [=table_size=](|store|, |tableaddr|). + 1. Let |addrtype| be the [=address type=] in [=table_type=](|store|, |tableaddr|). + 1. Let |length64| be [=table_size=](|store|, |tableaddr|). + 1. Return [=U64ToAddressValue=](|length64|, |addrtype|).
The get(|index|) method, when invoked, performs the following steps: 1. Let |tableaddr| be **this**.\[[Table]]. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. - 1. Let (limits, |elementType|) be [=table_type=](|store|, |tableaddr|). - 1. If |elementType| is [=exnref=], - 1. [=Throw=] a {{TypeError}} exception. - 1. Let |result| be [=table_read=](|store|, |tableaddr|, |index|). + 1. Let (|addrtype|, limits, |elementtype|) be [=table_type=](|store|, |tableaddr|). + 1. If |elementtype| is [=exnref=], + 1. Throw a {{TypeError}} exception. + 1. Let |index64| be [=?=] [=AddressValueToU64=](|index|, |addrtype|). + 1. Let |result| be [=table_read=](|store|, |tableaddr|, |index64|). 1. If |result| is [=error=], throw a {{RangeError}} exception. 1. Return [=ToJSValue=](|result|).
@@ -929,15 +1000,16 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address The set(|index|, |value|) method, when invoked, performs the following steps: 1. Let |tableaddr| be **this**.\[[Table]]. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. - 1. Let (limits, |elementType|) be [=table_type=](|store|, |tableaddr|). - 1. If |elementType| is [=exnref=], - 1. [=Throw=] a {{TypeError}} exception. + 1. Let (|addrtype|, limits, |elementtype|) be [=table_type=](|store|, |tableaddr|). + 1. If |elementtype| is [=exnref=], + 1. Throw a {{TypeError}} exception. + 1. Let |index64| be [=?=] [=AddressValueToU64=](|index|, |addrtype|). 1. If |value| is missing, - 1. Let |ref| be [=DefaultValue=](|elementType|). + 1. Let |ref| be [=DefaultValue=](|elementtype|). + 1. If |ref| is [=error=], throw a {{TypeError}} exception. 1. Otherwise, - 1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementType|). - 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. - 1. Let |store| be [=table_write=](|store|, |tableaddr|, |index|, |ref|). + 1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementtype|). + 1. Let |store| be [=table_write=](|store|, |tableaddr|, |index64|, |ref|). 1. If |store| is [=error=], throw a {{RangeError}} exception. 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. @@ -1010,13 +1082,8 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
The algorithm DefaultValue(|valuetype|) performs the following steps: - 1. If |valuetype| equals [=i32=], return [=i32.const=] 0. - 1. If |valuetype| equals [=i64=], return [=i64.const=] 0. - 1. If |valuetype| equals [=f32=], return [=f32.const=] 0. - 1. If |valuetype| equals [=f64=], return [=f64.const=] 0. - 1. If |valuetype| equals [=funcref=], return [=ref.null=] [=funcref=]. 1. If |valuetype| equals [=externref=], return [=ToWebAssemblyValue=](undefined, |valuetype|). - 1. Assert: This step is not reached. + 1. Return [=val_default=](|valuetype|).
@@ -1027,6 +1094,7 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each 1. Throw a {{TypeError}} exception. 1. If |v| is missing, 1. Let |value| be [=DefaultValue=](|valuetype|). + 1. Assert: |value| is not [=error=]. 1. Otherwise, 1. Let |value| be [=ToWebAssemblyValue=](|v|, |valuetype|). 1. If |mutable| is true, let |globaltype| be [=var=] |valuetype|; otherwise, let |globaltype| be [=const=] |valuetype|. @@ -1124,15 +1192,17 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [ 1. [=list/Append=] [=ToWebAssemblyValue=](|arg|, |t|) to |args|. 1. Set |i| to |i| + 1. 1. Let (|store|, |ret|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). - 1. Note: The expectation is that [=func_invoke=] will be updated to return (|store|, val* | [=error=] | (exception |exntag| |payload| |opaqueData|)). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. If |ret| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. - 1. If |ret| is exception |exntag| |payload| |opaqueData|, then - 1. If |opaqueData| is not [=ref.null=] [=externref=], - 1. Let « [=ref.extern=] |externaddr| » be |opaqueData|. - 1. Throw the result of [=retrieving an extern value=] from |externaddr|. - 1. Let |exception| be [=create an Exception object|a new Exception=] for |exntag| and |payload|. - 1. Throw |exception|. + 1. If |ret| is [=THROW=] [=ref.exn=] |exnaddr|, then + 1. Let |tagaddr| be [=exn_tag=](|store|, |exnaddr|). + 1. Let |payload| be [=exn_read=](|store|, |exnaddr|). + 1. Let |jsTagAddr| be the result of [=get the JavaScript exception tag |getting the JavaScript exception tag=]. + 1. If |tagaddr| is equal to |jsTagAddr|, + 1. Throw the result of [=retrieving a host value=] from |payload|[0]. + 1. Otherwise, + 1. Let |exception| be [=create an Exception object|a new Exception=] created from |exnaddr|. + 1. Throw |exception|. 1. Let |outArity| be the [=list/size=] of |ret|. 1. If |outArity| is 0, return undefined. 1. Otherwise, if |outArity| is 1, return [=ToJSValue=](|ret|[0]). @@ -1160,7 +1230,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. If |resultsSize| is 0, return « ». 1. Otherwise, if |resultsSize| is 1, return « [=?=] [=ToWebAssemblyValue=](|ret|, |results|[0]) ». 1. Otherwise, - 1. Let |method| be [=?=] [$GetMethod$](|ret|, {{@@iterator}}). + 1. Let |method| be [=?=] [$GetMethod$](|ret|, {{%Symbol.iterator%}}). 1. If |method| is undefined, [=throw=] a {{TypeError}}. 1. Let |values| be [=?=] [$IteratorToList$]([=?=] [$GetIteratorFromMethod$](|ret|, |method|)). 1. Let |wasmValues| be a new, empty [=list=]. @@ -1184,18 +1254,18 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. [=Clean up after running a callback=] with |stored settings|. 1. [=Clean up after running script=] with |relevant settings|. 1. Assert: |result|.\[[Type]] is throw or normal. + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. If |result|.\[[Type]] is throw, then: 1. Let |v| be |result|.\[[Value]]. 1. If |v| [=implements=] {{Exception}}, - 1. Let |type| be |v|.\[[Type]]. - 1. Let |payload| be |v|.\[[Payload]]. + 1. Let |address| be |v|.\[[Address]]. 1. Otherwise, - 1. Let |type| be the [=JavaScript exception tag=]. - 1. Let |payload| be « ». - 1. Let |opaqueData| be [=ToWebAssemblyValue=](|v|, [=externref=]) - 1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. + 1. Let |type| be the result of [=get the JavaScript exception tag |getting the JavaScript exception tag=]. + 1. Let |payload| be [=!=] [=ToWebAssemblyValue=](|v|, [=externref=]). + 1. Let (|store|, |address|) be [=exn_alloc=](|store|, |type|, « |payload| »). + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. Execute the WebAssembly instructions ([=ref.exn=] |address|) ([=throw_ref=]). 1. Otherwise, return |result|.\[[Value]]. - 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let (|store|, |funcaddr|) be [=func_alloc=](|store|, |functype|, |hostfunc|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. Return |funcaddr|. @@ -1208,19 +1278,19 @@ The algorithm ToJSValue(|w|) coerces a [=WebAssembly value=] to a Jav 1. Assert: |w| is not of the form [=ref.exn=] exnaddr. 1. If |w| is of the form [=i64.const=] |u64|, 1. Let |i64| be [=signed_64=](|u64|). - 1. Return [=ℤ=](|i64| interpreted as a mathematical value). + 1. Return [=ℤ=](|i64| interpreted as a [=mathematical value=]). 1. If |w| is of the form [=i32.const=] |u32|, - 1. Let |i32| be [=signed_32=](|i32|). - 2. Return [=𝔽=](|i32| interpreted as a mathematical value). + 1. Let |i32| be [=signed_32=](|u32|). + 2. Return [=𝔽=](|i32| interpreted as a [=mathematical value=]). 1. If |w| is of the form [=f32.const=] |f32|, 1. If |f32| is [=+∞=] or [=−∞=], return **+∞**𝔽 or **-∞**𝔽, respectively. 1. If |f32| is [=nan=], return **NaN**. - 1. Return [=𝔽=](|f32| interpreted as a mathematical value). + 1. Return [=𝔽=](|f32| interpreted as a [=mathematical value=]). 1. If |w| is of the form [=f64.const=] |f64|, 1. If |f64| is [=+∞=] or [=−∞=], return **+∞**𝔽 or **-∞**𝔽, respectively. 1. If |f64| is [=nan=], return **NaN**. - 1. Return [=𝔽=](|f64| interpreted as a mathematical value). -1. If |w| is of the form [=ref.null=] |t|, return null. + 1. Return [=𝔽=](|f64| interpreted as a [=mathematical value=]). +1. If |w| is of the form [=ref.null=] t, return null. 1. If |w| is of the form [=ref.i31=] |u31|, 1. Let |i31| be [=signed_31=](|u31|). 1. Let return [=𝔽=](|i31|). @@ -1228,7 +1298,7 @@ The algorithm ToJSValue(|w|) coerces a [=WebAssembly value=] to a Jav 1. If |w| is of the form [=ref.array=] |arrayaddr|, return the result of creating [=a new Exported GC Object=] from |arrayaddr| and "array". 1. If |w| is of the form [=ref.func=] |funcaddr|, return the result of creating [=a new Exported Function=] from |funcaddr|. 1. If |w| is of the form [=ref.host=] |hostaddr|, return the result of [=retrieving a host value=] from |hostaddr|. -1. If |w| is of the form [=ref.extern=] ref, return [=ToJSValue=](|ref|). +1. If |w| is of the form [=ref.extern=] |ref|, return [=ToJSValue=](|ref|). Note: Number values which are equal to NaN may have various observable NaN payloads; see [$NumericToRawBytes$] for details. @@ -1260,7 +1330,7 @@ The algorithm ToWebAssemblyValue(|v|, |type|) coerces a JavaScript va 1. If |type| is [=f32=], 1. Let |number| be [=?=] [$ToNumber$](|v|). 1. If |number| is **NaN**, - 1. Let |n| be an implementation-defined integer such that [=canon=]32 ≤ |n| < 2[=signif=](32). + 1. Let |n| be an implementation-defined integer such that [=canon=]32 ≤ |n| < 2[=signif=](32). 1. Let |f32| be [=nan=](n). 1. Otherwise, 1. Let |f32| be |number| rounded to the nearest representable value using IEEE 754-2008 round to nearest, ties to even mode. [[IEEE-754]] @@ -1268,7 +1338,7 @@ The algorithm ToWebAssemblyValue(|v|, |type|) coerces a JavaScript va 1. If |type| is [=f64=], 1. Let |number| be [=?=] [$ToNumber$](|v|). 1. If |number| is **NaN**, - 1. Let |n| be an implementation-defined integer such that [=canon=]64 ≤ |n| < 2[=signif=](64). + 1. Let |n| be an implementation-defined integer such that [=canon=]64 ≤ |n| < 2[=signif=](64). 1. Let |f64| be [=nan=](n). 1. Otherwise, 1. Let |f64| be |number|. @@ -1309,6 +1379,91 @@ The algorithm ToWebAssemblyValue(|v|, |type|) coerces a JavaScript va
+
+The algorithm AddressValueToU64(|v|, |addrtype|) converts a JavaScript value to a WebAssembly [=u64=] for use in embedding operations. It is designed to act like [=[EnforceRange]=] [=unsigned long=] for {{AddressType}} "i32", and to extend these semantics to {{AddressType}} "i64", by performing the following steps: + +1. If |addrtype| is "i32", + 1. Let |n| be [=?=] [$ConvertToInt$](|v|, 32, "unsigned"), where the destination type is associated with [=[EnforceRange]=]. + + Note: This is equivalent to the [=js-unsigned-long|JS conversion rules=] for [=[EnforceRange]=] [=unsigned long=]. + 1. Return [=ℝ=](|n|) as a WebAssembly [=u64=]. +1. If |addrtype| is "i64", + 1. Let |n| be [=?=] [$ToBigInt$](|v|). + 1. If |n| < 0 or |n| > 264 − 1, [=throw=] a {{TypeError}}. + + Note: This operation is designed to emulate [=[EnforceRange]=]. + 1. Return [=ℝ=](|n|) as a WebAssembly [=u64=]. +1. Assert: This step is not reached. + +
+ +
+The algorithm U64ToAddressValue(|v|, |addrtype|) converts a [=u64=] value from a WebAssembly embedding operation to the correct variant of {{AddressValue}} for an {{AddressType}}, by performing the following steps: + +1. If |addrtype| is "i32", return [=𝔽=](|v| interpreted as a [=mathematical value=]). +1. Else if |addrtype| is "i64", return [=ℤ=](|v| interpreted as a [=mathematical value=]). +1. Assert: This step is not reached. + +
+ +

Tags

+ +The tag_alloc(|store|, |parameters|) algorithm creates a new [=tag address=] for |parameters| in |store| and returns the updated store and the [=tag address=]. + +

Tag types

+ +
+dictionary TagType {
+  required sequence<ValueType> parameters;
+};
+
+[LegacyNamespace=WebAssembly, Exposed=(Window,Worker,Worklet)]
+interface Tag {
+  constructor(TagType type);
+};
+
+ +A {{Tag}} value represents an exception tag. + +
+ +To initialize a Tag object |tag| from a [=tag address=] |tagAddress|, perform the following steps: + +1. Let |map| be the [=surrounding agent=]'s associated [=Tag object cache=]. +1. Assert: |map|[|tagAddress|] doesn't [=map/exist=]. +1. Set |tag|.\[[Address]] to |tagAddress|. +1. [=map/Set=] |map|[|tagAddress|] to |tag|. + +
+ +
+To create a Tag object from a [=tag address=] |tagAddress|, perform the following steps: + +1. Let |map| be the [=surrounding agent=]'s associated [=Tag object cache=]. +1. If |map|[|tagAddress|] [=map/exists=], + 1. Return |map|[|tagAddress|]. +1. Let |tag| be a [=new=] {{Tag}}. +1. [=initialize a Tag object|Initialize=] |tag| from |tagAddress|. +1. Return |tag|. + +
+ +
+ +The new Tag(|type|) constructor steps are: + +1. Let |parameters| be |type|["parameters"]. +1. Let |wasmParameters| be «». +1. [=list/iterate|For each=] |type| of |parameters|, + 1. [=list/Append=] [=ToValueType=](|type|) to |wasmParameters|. +1. Let |store| be the current agent's [=associated store=]. +1. Let (|store|, |tagAddress|) be [=tag_alloc=](|store|, |wasmParameters|). +1. Set the current agent's [=associated store=] to |store|. +1. [=initialize a Tag object|Initialize=] **this** from |tagAddress|. + +
+ +

Garbage Collected Objects

A WebAssembly struct or array is made available in JavaScript as an Exported GC Object. @@ -1412,86 +1567,9 @@ The internal methods of an [=Exported GC Object=] use the following implementati 1. Set |object|.\[[OwnPropertyKeys]] as specified in [=[[OwnPropertyKeys]] internal method of an Exported GC Object=]. 1. [=map/Set=] |map|[|objectaddr|] to |object|. 1. Return |object|. - - - -

Tags

- -The tag_alloc(|store|, |parameters|) algorithm creates a new [=tag address=] for |parameters| in |store| and returns the updated store and the [=tag address=]. - -The tag_parameters(|store|, |tagAddress|) algorithm returns the [=list=] of types for |tagAddress| in |store|. - -

Exception types

- -
-dictionary TagType {
-  required sequence<ValueType> parameters;
-};
-
-[LegacyNamespace=WebAssembly, Exposed=(Window,Worker,Worklet)]
-interface Tag {
-  constructor(TagType type);
-  TagType type();
-};
-
- -A {{Tag}} value represents a type of exception. - -
- -To initialize a Tag object |tag| from a [=tag address=] |tagAddress|, perform the following steps: - -1. Let |map| be the [=surrounding agent=]'s associated [=Tag object cache=]. -1. Assert: |map|[|tagAddress|] doesn't [=map/exist=]. -1. Set |tag|.\[[Address]] to |tagAddress|. -1. [=map/Set=] |map|[|tagAddress|] to |tag|. - -
- -
- -To create a Tag object from a [=tag address=] |tagAddress|, perform the following steps: - -1. Let |map| be the [=surrounding agent=]'s associated [=Tag object cache=]. -1. If |map|[|tagAddress|] [=map/exists=], - 1. Return |map|[|tagAddress|]. -1. Let |tag| be a [=new=] {{Tag}}. -1. [=initialize a Tag object|Initialize=] |tag| from |tagAddress|. -1. Return |tag|. -
-
- -The new Tag(|type|) constructor steps are: - -1. Let |parameters| be |type|["parameters"]. -1. Let |wasmParameters| be «». -1. [=list/iterate|For each=] |type| of |parameters|, - 1. [=list/Append=] [=ToValueType=](|type|) to |wasmParameters|. -1. Let |store| be the current agent's [=associated store=]. -1. Let (|store|, |tagAddress|) be [=tag_alloc=](|store|, |wasmParameters|). -1. Set the current agent's [=associated store=] to |store|. -1. [=initialize a Tag object|Initialize=] **this** from |tagAddress|. - -
- -
- -The type() method steps are: - -1. Let |store| be the [=surrounding agent=]'s [=associated store=]. -1. Let |parameters| be [=tag_parameters=](|store|, **this**.\[[Address]]). -1. Let |idlParameters| be «». -1. [=list/iterate|For each=] |type| of |parameters|, - 1. [=list/Append=] [$FromValueType$](|type|) to |idlParameters|. -1. Return «[ "{{TagType/parameters}}" → |idlParameters| ]». - -Advisement: This method is only expected to be implemented or shipped when both this proposal and the Type Reflection proposal are implemented or shipped (respectively). - -
- -

Runtime exceptions

+

Exceptions

 dictionary ExceptionOptions {
@@ -1501,7 +1579,7 @@ dictionary ExceptionOptions {
 [LegacyNamespace=WebAssembly, Exposed=(Window,Worker,Worklet)]
 interface Exception {
   constructor(Tag exceptionTag, sequence<any> payload, optional ExceptionOptions options = {});
-  any getArg(Tag exceptionTag, [EnforceRange] unsigned long index);
+  any getArg([EnforceRange] unsigned long index);
   boolean is(Tag exceptionTag);
   readonly attribute (DOMString or undefined) stack;
 };
@@ -1511,19 +1589,32 @@ An {{Exception}} value represents an exception.
 
 
-To create an Exception object from a [=tag address=] |tagAddress| and a [=list=] of -WebAssembly values |payload|, perform the following steps: +To initialize an Exception object |exn| from an [=Exception address=] |exnAddress|, perform the following steps: +1. Let |map| be the [=surrounding agent=]'s associated [=Exception object cache=]. +1. Assert: |map|[|exnAddress|] doesn't [=map/exist=]. +1. Set |exn|.\[[Address]] to |exnAddress|. +1. [=map/Set=] |map|[|exnAddress|] to |exn|. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. -1. Let |types| be [=tag_parameters=](|store|, |tagAddress|). -1. Assert: |types|'s [=list/size=] is |payload|'s [=list/size=]. -1. [=list/iterate|For each=] |value| and |resultType| of |payload| and |types|, paired linearly, - 1. Assert: |value|'s type matches |resultType|. -1. Let |exception| be a [=new=] {{Exception}}. -1. Set |exception|.\[[Type]] to |tagAddress|. -1. Set |exception|.\[[Payload]] to |payload|. -1. Set |exception|.\[[Stack]] to undefined. -1. Return |exception|. +1. Let |tagaddr| be [=exn_tag=](|store|, |exnAddress|). +1. Let |payload| be [=exn_read=](|store|, |exnAddress|). +1. Set |exn|.\[[Type]] to |tagaddr|. +1. Set |exn|.\[[Payload]] to |payload|. +1. Set |exn|.\[[Stack]] to undefined. + +
+ +
+ +To create an Exception object from a [=exception address=] |exnAddress|, perform the following steps: + +1. Let |map| be the [=surrounding agent=]'s associated [=Exception object cache=]. +1. If |map|[|exnAddress|] [=map/exists=], + 1. Return |map|[|exnAddress|]. +1. Let |exn| be a [=new=] {{Exception}}. +1. [=initialize an Exception object|Initialize=] |exn| from |exnAddress|. +1. Return |exn|. +
@@ -1533,31 +1624,40 @@ The new Exception(|exceptionTag|, |payload|, |options|) constructor steps are: +1. Let |JSTagAddr| be the result of [=get the JavaScript exception tag|getting the JavaScript exception tag=]. +1. If |exceptionTag|.\[[Address]] is equal to |JSTagAddr|, + 1. Throw a {{TypeError}}. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. -1. Let |types| be [=tag_parameters=](|store|, |exceptionTag|.\[[Address]]). +1. Let [|types|] → [] be [=tag_type=](|store|, |exceptionTag|.\[[Address]]). 1. If |types|'s [=list/size=] is not |payload|'s [=list/size=], 1. Throw a {{TypeError}}. 1. Let |wasmPayload| be « ». 1. [=list/iterate|For each=] |value| and |resultType| of |payload| and |types|, paired linearly, - 1. [=list/Append=] ? [=ToWebAssemblyValue=](|value|, |resultType|) to |wasmPayload|. -1. Set **this**.\[[Type]] to |exceptionTag|.\[[Address]]. -1. Set **this**.\[[Payload]] to |wasmPayload|. + 1. If |resultType| is [=v128=] or [=exnref=], + 1. Throw a {{TypeError}}. + 1. [=list/Append=] [=?=] [=ToWebAssemblyValue=](|value|, |resultType|) to |wasmPayload|. +1. Let (|store|, |exceptionAddr|) be [=exn_alloc=](|store|, |exceptionTag|.\[[Address]], |wasmPayload|) +1. Set the [=surrounding agent=]'s [=associated store=] to |store|. +1. [=initialize an Exception object|Initialize=] **this** from |exceptionAddr|. 1. If |options|["traceStack"] is true, 1. Set **this**.\[[Stack]] to either a {{DOMString}} representation of the current call stack or undefined. -1. Otherwise, - 1. Set **this**.\[[Stack]] to undefined. +
-The getArg(|exceptionTag|, |index|) method steps are: +The getArg(|index|) method steps are: -1. If **this**.\[[Type]] is not equal to |exceptionTag|.\[[Address]], - 1. Throw a {{TypeError}}. -1. Let |payload| be **this**.\[[Payload]]. +1. Let |store| be the [=surrounding agent=]'s [=associated store=]. +1. Let |tagaddr| be [=exn_tag=](|store|, **this**.\[[Address]]). +1. Let |payload| be [=exn_read=](|store|, **this**.\[[Address]]). +1. Assert: |tagaddr| is equal to **this**.\[[Type]]. 1. If |index| ≥ |payload|'s [=list/size=], 1. Throw a {{RangeError}}. +1. Let [|types|] → [] be [=tag_type=](|store|, |tagaddr|). +1. If |types|[|index|] is [=v128=] or [=exnref=], + 1. Throw a {{TypeError}}. 1. Return [=ToJSValue=](|payload|[|index|]).
@@ -1582,20 +1682,22 @@ The stack getter steps are:

JavaScript exceptions

-The JavaScript exception tag is a [=tag address=] reserved by this -specification to distinguish exceptions originating from JavaScript. +The JavaScript exception tag is a [=tag address=] associated with +the surrounding agent. It is allocated in the agent's [=associated store=] on +first use and cached. It always has the [=tag type=] « [=externref=] » → « ». -For any [=associated store=] |store|, the result of -[=tag_parameters=](|store|, [=JavaScript exception tag=]) must be « ».
-To throw with a [=tag address=] |type|, a matching [=list=] of WebAssembly values |payload|, and an [=externref=] |opaqueData|, perform the following steps: +To get the JavaScript exception tag, perform the following steps: -1. Unwind the stack until reaching the *catching try block* given |type|. -1. Invoke the catch block with |payload| and |opaqueData|. - -Note: This algorithm is expected to be moved into the core specification. + 1. If the [=surrounding agent=]'s associated [=JavaScript exception tag=] has been initialized, + 1. return the [=surrounding agent=]'s associated [=JavaScript exception tag=] + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let (|store|, |tagAddress|) be [=tag_alloc=](|store|, « [=externref=] » → « »). + 1. Set the current agent's [=associated store=] to |store|. + 1. Set the current agent's associated [=JavaScript exception tag=] to |tagAddress|. + 1. return |tagAddress|.
@@ -1610,7 +1712,7 @@ When the [=namespace object=] for the {{WebAssembly}} namespace is [=create a na 1. Let |namespaceObject| be the [=namespace object=]. 1. [=list/iterate|For each=] |error| of « "CompileError", "LinkError", "RuntimeError" », 1. Let |constructor| be a new object, implementing the [=NativeError Object Structure=], with NativeError set to |error|. - 1. [=!=] [$CreateMethodProperty$](|namespaceObject|, |error|, |constructor|). + 1. [=!=] [$DefineMethodProperty$](|namespaceObject|, |error|, |constructor|, false). @@ -1709,3 +1811,29 @@ In practice, an implementation may run out of resources for valid modules below

This section is non-normative.

This document defines a host environment for WebAssembly. It enables a WebAssembly instance to [=import=] JavaScript objects and functions from an [=read the imports|import object=], but otherwise provides no access to the embedding environment. Thus a WebAssembly instance is bound to the same constraints as JavaScript. + +

Change History

+ +

This section is non-normative.

+ +

Since the original release 1.0 of the WebAssembly specification, a number of proposals for extensions have been integrated. +The following sections provide an overview of what has changed.

+ +

Release 2.0

+ +

Multiple Values

+Multiple values can be returned to and from JavaScript functions as an [=Array=] object. + +

BigInt Integration

+WebAssembly [=i64=] values can be passed to and from JavaScript (via imported or exported globals, table get or set operations, function return values or arguments) as [=BigInt=] objects. + +

Reference Types

+JavaScript values can be passed to and from WebAssembly (via imported or exported globals, table set or get operations, and function arguments or return values) as [=externref=] values. + +

Multiple Tables

+Multiple tables can be exported and imported to and from JavaScript. + +

Release 3.0

+ +

Multiple Memories

+Multiple memories can be exported and imported to and from JavaScript. diff --git a/document/legacy/exceptions/LICENSE b/document/legacy/exceptions/core/LICENSE similarity index 100% rename from document/legacy/exceptions/LICENSE rename to document/legacy/exceptions/core/LICENSE diff --git a/document/legacy/exceptions/Makefile b/document/legacy/exceptions/core/Makefile similarity index 100% rename from document/legacy/exceptions/Makefile rename to document/legacy/exceptions/core/Makefile diff --git a/document/legacy/exceptions/README.md b/document/legacy/exceptions/core/README.md similarity index 100% rename from document/legacy/exceptions/README.md rename to document/legacy/exceptions/core/README.md diff --git a/document/legacy/exceptions/appendix/index-instructions.py b/document/legacy/exceptions/core/appendix/index-instructions.py similarity index 100% rename from document/legacy/exceptions/appendix/index-instructions.py rename to document/legacy/exceptions/core/appendix/index-instructions.py diff --git a/document/legacy/exceptions/binary.rst b/document/legacy/exceptions/core/binary.rst similarity index 100% rename from document/legacy/exceptions/binary.rst rename to document/legacy/exceptions/core/binary.rst diff --git a/document/legacy/exceptions/conf.py b/document/legacy/exceptions/core/conf.py similarity index 100% rename from document/legacy/exceptions/conf.py rename to document/legacy/exceptions/core/conf.py diff --git a/document/legacy/exceptions/exec.rst b/document/legacy/exceptions/core/exec.rst similarity index 100% rename from document/legacy/exceptions/exec.rst rename to document/legacy/exceptions/core/exec.rst diff --git a/document/legacy/exceptions/index.rst b/document/legacy/exceptions/core/index.rst similarity index 100% rename from document/legacy/exceptions/index.rst rename to document/legacy/exceptions/core/index.rst diff --git a/document/legacy/exceptions/intro.rst b/document/legacy/exceptions/core/intro.rst similarity index 100% rename from document/legacy/exceptions/intro.rst rename to document/legacy/exceptions/core/intro.rst diff --git a/document/legacy/exceptions/static/custom.css b/document/legacy/exceptions/core/static/custom.css similarity index 100% rename from document/legacy/exceptions/static/custom.css rename to document/legacy/exceptions/core/static/custom.css diff --git a/document/legacy/exceptions/static/webassembly.png b/document/legacy/exceptions/core/static/webassembly.png similarity index 100% rename from document/legacy/exceptions/static/webassembly.png rename to document/legacy/exceptions/core/static/webassembly.png diff --git a/document/legacy/exceptions/syntax.rst b/document/legacy/exceptions/core/syntax.rst similarity index 100% rename from document/legacy/exceptions/syntax.rst rename to document/legacy/exceptions/core/syntax.rst diff --git a/document/legacy/exceptions/text.rst b/document/legacy/exceptions/core/text.rst similarity index 100% rename from document/legacy/exceptions/text.rst rename to document/legacy/exceptions/core/text.rst diff --git a/document/legacy/exceptions/util/macros.def b/document/legacy/exceptions/core/util/macros.def similarity index 100% rename from document/legacy/exceptions/util/macros.def rename to document/legacy/exceptions/core/util/macros.def diff --git a/document/legacy/exceptions/util/mathdef.py b/document/legacy/exceptions/core/util/mathdef.py similarity index 100% rename from document/legacy/exceptions/util/mathdef.py rename to document/legacy/exceptions/core/util/mathdef.py diff --git a/document/legacy/exceptions/util/pseudo-lexer.py b/document/legacy/exceptions/core/util/pseudo-lexer.py similarity index 100% rename from document/legacy/exceptions/util/pseudo-lexer.py rename to document/legacy/exceptions/core/util/pseudo-lexer.py diff --git a/document/legacy/exceptions/valid.rst b/document/legacy/exceptions/core/valid.rst similarity index 100% rename from document/legacy/exceptions/valid.rst rename to document/legacy/exceptions/core/valid.rst diff --git a/document/legacy/exceptions/js-api/Makefile b/document/legacy/exceptions/js-api/Makefile new file mode 100644 index 000000000..84ba5d3ab --- /dev/null +++ b/document/legacy/exceptions/js-api/Makefile @@ -0,0 +1,44 @@ +BUILDDIR = _build +STATICDIR = _static +DOWNLOADDIR = _download +NAME = WebAssembly + +.PHONY: all +all: + mkdir -p $(BUILDDIR)/html + bikeshed spec index.bs $(BUILDDIR)/html/index.html + @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html." + +.PHONY: publish +publish: + (cd ..; make publish-js-api) + +.PHONY: clean +clean: + rm -rf $(BUILDDIR) + rm -rf $(STATICDIR) + +.PHONY: diff +diff: all + @echo "Downloading the old single-file html spec..." + curl `grep "^TR" index.bs | cut -d' ' -f2` -o $(BUILDDIR)/html/old.html + @echo "Done." + @echo "Diffing new against old..." + perl ../util/htmldiff.pl $(BUILDDIR)/html/old.html $(BUILDDIR)/html/index.html $(BUILDDIR)/html/diff.html + @echo "Done. The diff is at $(BUILDDIR)/html/diff.html" + +.PHONY: WD-tar +WD-tar: + bikeshed echidna --just-tar index.bs $(BUILDDIR)/html/index.html + mv test.tar $(BUILDDIR)/WD.tar + @echo "Built $(BUILDDIR)/WD.tar." + +.PHONY: WD-echidna +WD-echidna: + @if [ -z $(W3C_USERNAME) ] || \ + [ -z $(W3C_PASSWORD) ] || \ + [ -z $(DECISION_URL) ] ; then \ + echo "Must provide W3C_USERNAME, W3C_PASSWORD, and DECISION_URL environment variables"; \ + exit 1; \ + fi + bikeshed echidna index.bs --u $(W3C_USERNAME) --p $(W3C_PASSWORD) --d $(DECISION_URL) diff --git a/document/legacy/exceptions/js-api/index.bs b/document/legacy/exceptions/js-api/index.bs new file mode 100644 index 000000000..e7db14074 --- /dev/null +++ b/document/legacy/exceptions/js-api/index.bs @@ -0,0 +1,1450 @@ + + +
+{
+  "WEBASSEMBLY": {
+    "href": "https://webassembly.github.io/spec/core/",
+    "title": "WebAssembly Core Specification",
+    "publisher": "W3C WebAssembly Community Group",
+    "status": "Draft"
+  }
+}
+
+ +
+urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
+    type: interface; for: ECMAScript
+        text: ArrayBuffer; url: sec-arraybuffer-objects
+    type: exception; for: ECMAScript
+        text: Error; url: sec-error-objects
+        text: NativeError; url: sec-nativeerror-constructors
+        text: TypeError; url: sec-native-error-types-used-in-this-standard-typeerror
+        text: RangeError; url: sec-native-error-types-used-in-this-standard-rangeerror
+    type: dfn
+        url: sec-returnifabrupt-shorthands
+            text: !
+            text: ?
+        text: Type; url: sec-ecmascript-data-types-and-values
+        text: current Realm; url: current-realm
+        text: Built-in Function Objects; url: sec-built-in-function-objects
+        text: NativeError Object Structure; url: sec-nativeerror-object-structure
+        text: 𝔽; url: #𝔽
+        text: ℤ; url: #ℤ
+urlPrefix: https://webassembly.github.io/exception-handling/core/; spec: WebAssembly; type: dfn
+    url: valid/modules.html#valid-module
+        text: valid
+        text: WebAssembly module validation
+    text: module grammar; url: binary/modules.html#binary-module
+    text: custom section; url: binary/modules.html#custom-section
+    text: customsec; url: binary/modules.html#binary-customsec
+    text: memory instance; url: exec/runtime.html#memory-instances
+    text: table instance; url: exec/runtime.html#table-instances
+    text: global instance; url: exec/runtime.html#global-instances
+    text: trap; url: exec/runtime.html#syntax-trap
+    url: exec/runtime.html#values
+        text: WebAssembly value
+        text: i64.const
+        text: i32.const
+        text: f32.const
+        text: f64.const
+        text: v128.const
+        text: ref.null
+        text: ref.func
+        text: ref.extern
+    text: function index; url: syntax/modules.html#syntax-funcidx
+    text: function instance; url: exec/runtime.html#function-instances
+    text: store_init; url: appendix/embedding.html#embed-store-init
+    text: module_decode; url: appendix/embedding.html#embed-module-decode
+    text: module_validate; url: appendix/embedding.html#embed-module-validate
+    text: module_instantiate; url: appendix/embedding.html#embed-module-instantiate
+    text: module_imports; url: appendix/embedding.html#embed-module-imports
+    text: module_exports; url: appendix/embedding.html#embed-module-exports
+    text: instance_export; url: appendix/embedding.html#embed-instance-export
+    text: func_alloc; url: appendix/embedding.html#embed-func-alloc
+    text: func_type; url: appendix/embedding.html#embed-func-type
+    text: func_invoke; url: appendix/embedding.html#embed-func-invoke
+    text: table_alloc; url: appendix/embedding.html#embed-table-alloc
+    text: table_type; url: appendix/embedding.html#embed-table-type
+    text: table_read; url: appendix/embedding.html#embed-table-read
+    text: table_write; url: appendix/embedding.html#embed-table-write
+    text: table_size; url: appendix/embedding.html#embed-table-size
+    text: table_grow; url: appendix/embedding.html#embed-table-grow
+    text: mem_alloc; url: appendix/embedding.html#embed-mem-alloc
+    text: mem_type; url: appendix/embedding.html#embed-mem-type
+    text: mem_read; url: appendix/embedding.html#embed-mem-read
+    text: mem_write; url: appendix/embedding.html#embed-mem-write
+    text: mem_size; url: appendix/embedding.html#embed-mem-size
+    text: mem_grow; url: appendix/embedding.html#embed-mem-grow
+    text: global_alloc; url: appendix/embedding.html#embed-global-alloc
+    text: global_type; url: appendix/embedding.html#embed-global-type
+    text: global_read; url: appendix/embedding.html#embed-global-read
+    text: global_write; url: appendix/embedding.html#embed-global-write
+    text: error; url: appendix/embedding.html#embed-error
+    text: store; url: exec/runtime.html#syntax-store
+    text: table type; url: syntax/types.html#syntax-tabletype
+    text: table address; url: exec/runtime.html#syntax-tableaddr
+    text: function address; url: exec/runtime.html#syntax-funcaddr
+    text: memory address; url: exec/runtime.html#syntax-memaddr
+    text: global address; url: exec/runtime.html#syntax-globaladdr
+    text: extern address; url: exec/runtime.html#syntax-externaddr
+    text: tag address; url: exec/runtime.html#syntax-tagaddr
+    url: syntax/types.html#syntax-numtype
+        text: i32
+        text: i64
+        text: f32
+        text: f64
+    url: syntax/types.html#vector-types
+        text: v128
+    url: syntax/types.html#syntax-reftype
+        text: reftype
+        text: funcref
+        text: externref
+    url: syntax/values.html#syntax-float
+        text: +∞
+        text: −∞
+        text: nan
+        text: canon
+        text: signif
+    text: function element; url: exec/runtime.html#syntax-funcelem
+    text: import component; url: syntax/modules.html#imports
+    url: exec/runtime.html#syntax-externval
+        text: external value
+        for: external value
+            text: tag
+    text: host function; url: exec/runtime.html#syntax-hostfunc
+    text: the instantiation algorithm; url: exec/modules.html#instantiation
+    text: module; url: syntax/modules.html#syntax-module
+    text: imports; url: syntax/modules.html#syntax-module
+    text: import; url: syntax/modules.html#syntax-import
+    url: syntax/types.html#external-types
+        text: external type
+        text: func
+        text: table
+        text: mem
+        text: global
+        for: externtype
+            text: tag
+    text: global type; url: syntax/types.html#syntax-globaltype
+    url: syntax/types.html#syntax-mut
+        text: var
+        text: const
+    text: address; url: exec/runtime.html#addresses
+    text: signed_32; url: exec/numerics.html#aux-signed
+    text: memory.grow; url: exec/instructions.html#exec-memory-grow
+    text: current frame; url: exec/conventions.html#exec-notation-textual
+    text: module; for: frame; url: exec/runtime.html#syntax-frame
+    text: memaddrs; for: moduleinst; url: exec/runtime.html#syntax-moduleinst
+    text: signed_64; url: exec/numerics.html#aux-signed
+    text: sequence; url: syntax/conventions.html#grammar-notation
+    text: exception; for: tagtype/attribute; url: syntax/types.html#syntax-tagtype
+urlPrefix: https://heycam.github.io/webidl/; spec: WebIDL
+    type: dfn
+        text: create a namespace object; url: create-a-namespace-object
+urlPrefix: https://webassembly.github.io/js-types/js-api/; spec: WebAssembly JS API (JS Type Reflection)
+    type: abstract-op; text: FromValueType; url: abstract-opdef-fromvaluetype
+
+ + + + + +This API provides a way to access WebAssembly [[WEBASSEMBLY]] through a bridge to explicitly construct modules from JavaScript [[ECMASCRIPT]]. + +

Sample API Usage

+ +

This section is non-normative.

+ +Given `demo.wat` (encoded to `demo.wasm`): + +```lisp +(module + (import "js" "import1" (func $i1)) + (import "js" "import2" (func $i2)) + (func $main (call $i1)) + (start $main) + (func (export "f") (call $i2)) +) +``` + +and the following JavaScript, run in a browser: + +```javascript +var importObj = {js: { + import1: () => console.log("hello,"), + import2: () => console.log("world!") +}}; +fetch('demo.wasm').then(response => + response.arrayBuffer() +).then(buffer => + WebAssembly.instantiate(buffer, importObj) +).then(({module, instance}) => + instance.exports.f() +); +``` + +

Notation

+ +This specification depends on the Infra Standard. [[INFRA]] + +The WebAssembly [=sequence=] type is equivalent to the [=list=] type defined there; values of one +are treated as values of the other transparently. + +

Internal storage

+ +

Interaction of the WebAssembly Store with JavaScript

+ +Note: WebAssembly semantics are defined in terms of an abstract [=store=], representing the state of the WebAssembly abstract machine. WebAssembly operations take a store and return an updated store. + +Each [=agent=] has an associated store. When a new agent is created, its associated store is set to the result of [=store_init=](). + +Note: In this specification, no WebAssembly-related objects, memory or addresses can be shared among agents in an [=agent cluster=]. In a future version of WebAssembly, this may change. + +Elements of the WebAssembly store may be identified with JavaScript values. In particular, each WebAssembly [=memory instance=] with a corresponding {{Memory}} object is identified with a JavaScript [=Data Block=]; modifications to this Data Block are identified to updating the agent's store to a store which reflects those changes, and vice versa. + +

WebAssembly JS Object Caches

+ +Note: There are several WebAssembly objects that may have a corresponding JavaScript object. The correspondence is stored in a per-agent mapping from WebAssembly [=address=]es to JavaScript objects. +This mapping is used to ensure that, for a given [=agent=], there exists at most one JavaScript object for a particular WebAssembly address. However, this property does not hold for shared objects. + +Each [=agent=] is associated with the following [=ordered map=]s: + * The Memory object cache, mapping [=memory address=]es to {{Memory}} objects. + * The Table object cache, mapping [=table address=]es to {{Table}} objects. + * The Exported Function cache, mapping [=function address=]es to [=Exported Function=] objects. + * The Global object cache, mapping [=global address=]es to {{Global}} objects. + * The Extern value cache, mapping [=extern address=]es to values. + * The Tag object cache, mapping [=tag addresses=] to {{Tag}} objects. + +

The WebAssembly Namespace

+ +
+dictionary WebAssemblyInstantiatedSource {
+    required Module module;
+    required Instance instance;
+};
+
+[Exposed=*]
+namespace WebAssembly {
+    boolean validate(BufferSource bytes);
+    Promise<Module> compile(BufferSource bytes);
+
+    Promise<WebAssemblyInstantiatedSource> instantiate(
+        BufferSource bytes, optional object importObject);
+
+    Promise<Instance> instantiate(
+        Module moduleObject, optional object importObject);
+};
+
+ + + +
+ To compile a WebAssembly module from source bytes |bytes|, perform the following steps: + 1. Let |module| be [=module_decode=](|bytes|). If |module| is [=error=], return [=error=]. + 1. If [=module_validate=](|module|) is [=error=], return [=error=]. + 1. Return |module|. +
+ +
+ The validate(|bytes|) method, when invoked, performs the following steps: + 1. Let |stableBytes| be a [=get a copy of the buffer source|copy of the bytes held by the buffer=] |bytes|. + 1. [=Compile a WebAssembly module|Compile=] |stableBytes| as a WebAssembly module and store the results as |module|. + 1. If |module| is [=error=], return false. + 1. Return true. +
+ +A {{Module}} object represents a single WebAssembly module. Each {{Module}} object has the following internal slots: + + * \[[Module]] : a WebAssembly [=/module=] + * \[[Bytes]] : the source bytes of \[[Module]]. + +
+ To construct a WebAssembly module object from a module |module| and source bytes |bytes|, perform the following steps: + + 1. Let |moduleObject| be a new {{Module}} object. + 1. Set |moduleObject|.\[[Module]] to |module|. + 1. Set |moduleObject|.\[[Bytes]] to |bytes|. + 1. Return |moduleObject|. +
+ +
+ To asynchronously compile a WebAssembly module from source bytes |bytes|, using optional [=task source=] |taskSource|, perform the following steps: + + 1. Let |promise| be [=a new promise=]. + 1. Run the following steps [=in parallel=]: + 1. [=compile a WebAssembly module|Compile the WebAssembly module=] |bytes| and store the result as |module|. + 1. [=Queue a task=] to perform the following steps. If |taskSource| was provided, queue the task on that task source. + 1. If |module| is [=error=], reject |promise| with a {{CompileError}} exception. + 1. Otherwise, + 1. [=Construct a WebAssembly module object=] from |module| and |bytes|, and let |moduleObject| be the result. + 1. [=Resolve=] |promise| with |moduleObject|. + 1. Return |promise|. +
+ +
+ The compile(|bytes|) method, when invoked, performs the following steps: + 1. Let |stableBytes| be a [=get a copy of the buffer source|copy of the bytes held by the buffer=] |bytes|. + 1. [=Asynchronously compile a WebAssembly module=] from |stableBytes| and return the result. +
+ +
+ To read the imports from a WebAssembly module |module| from imports object |importObject|, perform the following steps: + 1. If |module|.[=imports=] [=list/is empty|is not empty=], and |importObject| is undefined, throw a {{TypeError}} exception. + 1. Let |imports| be « ». + 1. [=list/iterate|For each=] (|moduleName|, |componentName|, |externtype|) of [=module_imports=](|module|), + 1. Let |o| be [=?=] [$Get$](|importObject|, |moduleName|). + 1. If [=Type=](|o|) is not Object, throw a {{TypeError}} exception. + 1. Let |v| be [=?=] [$Get$](|o|, |componentName|). + 1. If |externtype| is of the form [=func=] |functype|, + 1. If [$IsCallable$](|v|) is false, throw a {{LinkError}} exception. + 1. If |v| has a \[[FunctionAddress]] internal slot, and therefore is an [=Exported Function=], + 1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot. + 1. Otherwise, + 1. [=Create a host function=] from |v| and |functype|, and let |funcaddr| be the result. + 1. Let |index| be the number of external functions in |imports|. This value |index| is known as the index of the host function |funcaddr|. + 1. Let |externfunc| be the [=external value=] [=external value|func=] |funcaddr|. + 1. [=list/Append=] |externfunc| to |imports|. + 1. If |externtype| is of the form [=global=] mut |valtype|, + 1. If [=Type=](|v|) is Number or BigInt, + 1. If |valtype| is [=i64=] and [=Type=](|v|) is Number, + 1. Throw a {{LinkError}} exception. + 1. If |valtype| is not [=i64=] and [=Type=](|v|) is BigInt, + 1. Throw a {{LinkError}} exception. + 1. If |valtype| is [=v128=], + 1. Throw a {{LinkError}} exception. + 1. Let |value| be [=ToWebAssemblyValue=](|v|, |valtype|). + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, [=const=] |valtype|, |value|). + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. Otherwise, if |v| [=implements=] {{Global}}, + 1. Let |globaladdr| be |v|.\[[Global]]. + 1. Otherwise, + 1. Throw a {{LinkError}} exception. + 1. Let |externglobal| be [=external value|global=] |globaladdr|. + 1. [=list/Append=] |externglobal| to |imports|. + 1. If |externtype| is of the form [=mem=] memtype, + 1. If |v| does not [=implement=] {{Memory}}, throw a {{LinkError}} exception. + 1. Let |externmem| be the [=external value=] [=external value|mem=] |v|.\[[Memory]]. + 1. [=list/Append=] |externmem| to |imports|. + 1. If |externtype| is of the form [=table=] tabletype, + 1. If |v| does not [=implement=] {{Table}}, throw a {{LinkError}} exception. + 1. Let |tableaddr| be |v|.\[[Table]]. + 1. Let |externtable| be the [=external value=] [=external value|table=] |tableaddr|. + 1. [=list/Append=] |externtable| to |imports|. + 1. If |externtype| is of the form [=externtype/tag=] |attribute| functype, + 1. Assert: |attribute| is [=tagtype/attribute/exception=]. + 1. If |v| does not [=implement=] {{Tag}}, throw a {{LinkError}} exception. + 1. Let |tagaddr| be |v|.\[[Address]]. + 1. Let |externtag| be the [=external value=] [=external value/tag=] |tagaddr|. + 1. [=list/Append=] |externtag| to |imports|. + 1. Return |imports|. + +Note: This algorithm only verifies the right kind of JavaScript values are passed. +The verification of WebAssembly type requirements is deferred to the +"[=instantiate the core of a WebAssembly module=]" algorithm. +
+ +
+ To create an exports object from a WebAssembly module |module| and instance |instance|, perform the following steps: + 1. Let |exportsObject| be [=!=] [$OrdinaryObjectCreate$](null). + 1. [=list/iterate|For each=] (|name|, |externtype|) of [=module_exports=](|module|), + 1. Let |externval| be [=instance_export=](|instance|, |name|). + 1. Assert: |externval| is not [=error=]. + 1. If |externtype| is of the form [=func=] functype, + 1. Assert: |externval| is of the form [=external value|func=] |funcaddr|. + 1. Let [=external value|func=] |funcaddr| be |externval|. + 1. Let |func| be the result of creating [=a new Exported Function=] from |funcaddr|. + 1. Let |value| be |func|. + 1. If |externtype| is of the form [=global=] mut globaltype, + 1. Assert: |externval| is of the form [=external value|global=] |globaladdr|. + 1. Let [=external value|global=] |globaladdr| be |externval|. + 1. Let |global| be [=create a global object|a new Global object=] created from |globaladdr|. + 1. Let |value| be |global|. + 1. If |externtype| is of the form [=mem=] memtype, + 1. Assert: |externval| is of the form [=external value|mem=] |memaddr|. + 1. Let [=external value|mem=] |memaddr| be |externval|. + 1. Let |memory| be [=create a memory object|a new Memory object=] created from |memaddr|. + 1. Let |value| be |memory|. + 1. If |externtype| is of the form [=table=] tabletype, + 1. Assert: |externval| is of the form [=external value|table=] |tableaddr|. + 1. Let [=external value|table=] |tableaddr| be |externval|. + 1. Let |table| be [=create a Table object|a new Table object=] created from |tableaddr|. + 1. Let |value| be |table|. + 1. If |externtype| is of the form [=externtype/tag=] |attribute| functype, + 1. Assert: |attribute| is [=tagtype/attribute/exception=]. + 1. Assert: |externval| is of the form [=external value/tag=] |tagaddr|. + 1. Let [=external value/tag=] |tagaddr| be |externval|. + 1. Let |tag| be [=create a Tag object|a new Tag object=] created from |tagaddr|. + 1. Let |value| be |tag|. + 1. Let |status| be [=!=] [$CreateDataProperty$](|exportsObject|, |name|, |value|). + 1. Assert: |status| is true. + + Note: the validity and uniqueness checks performed during [=WebAssembly module validation=] ensure that each property name is valid and no properties are defined twice. + 1. Perform [=!=] [$SetIntegrityLevel$](|exportsObject|, `"frozen"`). + 1. Return |exportsObject|. +
+ +
+ To initialize an instance object |instanceObject| from a WebAssembly module |module| and instance |instance|, perform the following steps: + + 1. [=Create an exports object=] from |module| and |instance| and let |exportsObject| be the result. + 1. Set |instanceObject|.\[[Instance]] to |instance|. + 1. Set |instanceObject|.\[[Exports]] to |exportsObject|. +
+ +
+ To instantiate the core of a WebAssembly module from a module |module| and imports |imports|, perform the following steps: + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |result| be [=module_instantiate=](|store|, |module|, |imports|). + 1. If |result| is [=error=], throw an appropriate exception type: + * A {{LinkError}} exception for most cases which occur during linking. + * If the error came when running the start function, throw a {{RuntimeError}} for most errors which occur from WebAssembly, or the error object propagated from inner ECMAScript code. + * Another error type if appropriate, for example an out-of-memory exception, as documented in the WebAssembly error mapping. + 1. Let (|store|, |instance|) be |result|. + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. Return |instance|. +
+ +
+ To asynchronously instantiate a WebAssembly module from a {{Module}} |moduleObject| and imports |importObject|, perform the following steps: + 1. Let |promise| be [=a new promise=]. + 1. Let |module| be |moduleObject|.\[[Module]]. + 1. [=Read the imports=] of |module| with imports |importObject|, and let |imports| be the result. + If this operation throws an exception, catch it, [=reject=] |promise| with the exception, and return |promise|. + 1. Run the following steps [=in parallel=]: + 1. [=Queue a task=] to perform the following steps: + Note: Implementation-specific work may be performed here. + 1. [=Instantiate the core of a WebAssembly module=] |module| with |imports|, and let |instance| be the result. + If this throws an exception, catch it, [=reject=] |promise| with the exception, and terminate these substeps. + 1. Let |instanceObject| be a [=/new=] {{Instance}}. + 1. [=initialize an instance object|Initialize=] |instanceObject| from |module| and |instance|. + If this throws an exception, catch it, [=reject=] |promise| with the exception, and terminate these substeps. + 1. [=Resolve=] |promise| with |instanceObject|. + 1. Return |promise|. +
+ +
+ To synchronously instantiate a WebAssembly module from a {{Module}} |moduleObject| and imports |importObject|, perform the following steps: + 1. Let |module| be |moduleObject|.\[[Module]]. + 1. [=Read the imports=] of |module| with imports |importObject|, and let |imports| be the result. + 1. [=Instantiate the core of a WebAssembly module=] |module| with |imports|, and let |instance| be the result. + 1. Let |instanceObject| be a [=/new=] {{Instance}}. + 1. [=initialize an instance object|Initialize=] |instanceObject| from |module| and |instance|. + 1. Return |instanceObject|. +
+ +
+ To instantiate a promise of a module |promiseOfModule| with imports |importObject|, perform the following steps: + + 1. Let |promise| be [=a new promise=]. + 1. [=Upon fulfillment=] of |promiseOfModule| with value |module|: + 1. [=asynchronously instantiate a WebAssembly module|Instantiate the WebAssembly module=] |module| importing |importObject|, and let |innerPromise| be the result. + 1. [=Upon fulfillment=] of |innerPromise| with value |instance|. + 1. Let |result| be the {{WebAssemblyInstantiatedSource}} value «[ "{{WebAssemblyInstantiatedSource/module}}" → |module|, "{{WebAssemblyInstantiatedSource/instance}}" → |instance| ]». + 1. [=Resolve=] |promise| with |result|. + 1. [=Upon rejection=] of |innerPromise| with reason |reason|: + 1. [=Reject=] |promise| with |reason|. + 1. [=Upon rejection=] of |promiseOfModule| with reason |reason|: + 1. [=Reject=] |promise| with |reason|. + 1. Return |promise|. +
+ +
+ The instantiate(|bytes|, |importObject|) method, when invoked, performs the following steps: + 1. Let |stableBytes| be a [=get a copy of the buffer source|copy of the bytes held by the buffer=] |bytes|. + 1. [=Asynchronously compile a WebAssembly module=] from |stableBytes| and let |promiseOfModule| be the result. + 1. [=Instantiate a promise of a module|Instantiate=] |promiseOfModule| with imports |importObject| and return the result. +
+ +
+ The instantiate(|moduleObject|, |importObject|) method, when invoked, performs the following steps: + 1. [=asynchronously instantiate a WebAssembly module|Asynchronously instantiate the WebAssembly module=] |moduleObject| importing |importObject|, and return the result. +
+ +Note: A follow-on streaming API is documented in the WebAssembly Web API. + +

Modules

+ +
+enum ImportExportKind {
+  "function",
+  "table",
+  "memory",
+  "global",
+  "tag"
+};
+
+dictionary ModuleExportDescriptor {
+  required USVString name;
+  required ImportExportKind kind;
+  // Note: Other fields such as signature may be added in the future.
+};
+
+dictionary ModuleImportDescriptor {
+  required USVString module;
+  required USVString name;
+  required ImportExportKind kind;
+};
+
+[LegacyNamespace=WebAssembly, Exposed=*]
+interface Module {
+  constructor(BufferSource bytes);
+  static sequence<ModuleExportDescriptor> exports(Module moduleObject);
+  static sequence<ModuleImportDescriptor> imports(Module moduleObject);
+  static sequence<ArrayBuffer> customSections(Module moduleObject, DOMString sectionName);
+};
+
+ +
+ The string value of the extern type |type| is + * "function" if |type| is of the form [=func=] functype + * "table" if |type| is of the form [=table=] tabletype + * "memory" if |type| is of the form [=mem=] memtype + * "global" if |type| is of the form [=global=] globaltype + * "tag" if |type| is of the form [=externtype/tag=] tag +
+ +
+ The exports(|moduleObject|) method, when invoked, performs the following steps: + 1. Let |module| be |moduleObject|.\[[Module]]. + 1. Let |exports| be « ». + 1. [=list/iterate|For each=] (|name|, |type|) of [=module_exports=](|module|), + 1. Let |kind| be the [=string value of the extern type=] |type|. + 1. Let |obj| be «[ "{{ModuleExportDescriptor/name}}" → |name|, "{{ModuleExportDescriptor/kind}}" → |kind| ]». + 1. [=list/Append=] |obj| to |exports|. + 1. Return |exports|. +
+ +
+ The imports(|moduleObject|) method, when invoked, performs the following steps: + 1. Let |module| be |moduleObject|.\[[Module]]. + 1. Let |imports| be « ». + 1. [=list/iterate|For each=] (|moduleName|, |name|, |type|) of [=module_imports=](|module|), + 1. Let |kind| be the [=string value of the extern type=] |type|. + 1. Let |obj| be «[ "{{ModuleImportDescriptor/module}}" → |moduleName|, "{{ModuleImportDescriptor/name}}" → |name|, "{{ModuleImportDescriptor/kind}}" → |kind| ]». + 1. [=list/Append=] |obj| to |imports|. + 1. Return |imports|. +
+ +
+ The customSections(|moduleObject|, |sectionName|) method, when invoked, performs the following steps: + 1. Let |bytes| be |moduleObject|.\[[Bytes]]. + 1. Let |customSections| be « ». + 1. [=list/iterate|For each=] [=custom section=] |customSection| of |bytes|, interpreted according to the [=module grammar=], + 1. Let |name| be the name of |customSection|, [=UTF-8 decode without BOM or fail|decoded as UTF-8=]. + 1. Assert: |name| is not failure (|moduleObject|.\[[Module]] is [=valid=]). + 1. If |name| equals |sectionName| as string values, + 1. [=list/Append=] a new {{ArrayBuffer}} containing a copy of the bytes in |bytes| for the range matched by this [=customsec=] production to |customSections|. + 1. Return |customSections|. +
+ +
+ The Module(|bytes|) constructor, when invoked, performs the following steps: + + 1. Let |stableBytes| be a [=get a copy of the buffer source|copy of the bytes held by the buffer=] |bytes|. + 1. [=Compile a WebAssembly module|Compile the WebAssembly module=] |stableBytes| and store the result as |module|. + 1. If |module| is [=error=], throw a {{CompileError}} exception. + 1. Set **this**.\[[Module]] to |module|. + 1. Set **this**.\[[Bytes]] to |stableBytes|. + +Note: Some implementations enforce a size limitation on |bytes|. Use of this API is discouraged, in favor of asynchronous APIs. +
+ +

Instances

+ +
+[LegacyNamespace=WebAssembly, Exposed=*]
+interface Instance {
+  constructor(Module module, optional object importObject);
+  readonly attribute object exports;
+};
+
+ +
+ The Instance(|module|, |importObject|) constructor, when invoked, runs the following steps: + 1. Let |module| be |module|.\[[Module]]. + 1. [=Read the imports=] of |module| with imports |importObject|, and let |imports| be the result. + 1. [=Instantiate the core of a WebAssembly module=] |module| with |imports|, and let |instance| be the result. + 1. [=initialize an instance object|Initialize=] **this** from |module| and |instance|. + +Note: The use of this synchronous API is discouraged, as some implementations sometimes do long-running compilation work when instantiating. +
+ +
+ The getter of the exports attribute of {{Instance}} returns **this**.\[[Exports]]. +
+ +

Memories

+ +
+dictionary MemoryDescriptor {
+  required [EnforceRange] unsigned long initial;
+  [EnforceRange] unsigned long maximum;
+};
+
+[LegacyNamespace=WebAssembly, Exposed=*]
+interface Memory {
+  constructor(MemoryDescriptor descriptor);
+  unsigned long grow([EnforceRange] unsigned long delta);
+  readonly attribute ArrayBuffer buffer;
+};
+
+ +A {{Memory}} object represents a single [=memory instance=] +which can be simultaneously referenced by multiple {{Instance}} objects. Each +{{Memory}} object has the following internal slots: + + * \[[Memory]] : a [=memory address=] + * \[[BufferObject]] : an {{ArrayBuffer}} whose [=Data Block=] is [=identified with=] the above memory address + +
+ To create a memory buffer from a [=memory address=] |memaddr|, perform the following steps: + + 1. Let |block| be a [=Data Block=] which is [=identified with=] the underlying memory of |memaddr|. + 1. Let |buffer| be a new {{ArrayBuffer}} whose \[[ArrayBufferData]] is |block| and \[[ArrayBufferByteLength]] is set to the length of |block|. + 1. Set |buffer|.\[[ArrayBufferDetachKey]] to "WebAssembly.Memory". + 1. Return |buffer|. +
+ +
+ To initialize a memory object |memory| from a [=memory address=] |memaddr|, perform the following steps: + 1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=]. + 1. Assert: |map|[|memaddr|] doesn't [=map/exist=]. + 1. Let |buffer| be the result of [=create a memory buffer|creating a memory buffer=] from |memaddr|. + 1. Set |memory|.\[[Memory]] to |memaddr|. + 1. Set |memory|.\[[BufferObject]] to |buffer|. + 1. [=map/Set=] |map|[|memaddr|] to |memory|. +
+ +
+ To create a memory object from a [=memory address=] |memaddr|, perform the following steps: + + 1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=]. + 1. If |map|[|memaddr|] [=map/exists=], + 1. Return |map|[|memaddr|]. + 1. Let |memory| be a [=/new=] {{Memory}}. + 1. [=initialize a memory object|Initialize=] |memory| from |memaddr|. + 1. Return |memory|. +
+ +
+ The Memory(|descriptor|) constructor, when invoked, performs the following steps: + 1. Let |initial| be |descriptor|["initial"]. + 1. If |descriptor|["maximum"] [=map/exists=], let |maximum| be |descriptor|["maximum"]; otherwise, let |maximum| be empty. + 1. If |maximum| is not empty and |maximum| < |initial|, throw a {{RangeError}} exception. + 1. Let |memtype| be { min |initial|, max |maximum| }. + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let (|store|, |memaddr|) be [=mem_alloc=](|store|, |memtype|). If allocation fails, throw a {{RangeError}} exception. + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. [=initialize a memory object|Initialize=] **this** from |memaddr|. +
+ +
+ To reset the Memory buffer of |memaddr|, perform the following steps: + + 1. Let |map| be the [=surrounding agent=]'s associated [=Memory object cache=]. + 1. Assert: |map|[|memaddr|] [=map/exists=]. + 1. Let |memory| be |map|[|memaddr|]. + 1. Perform [=!=] [$DetachArrayBuffer$](|memory|.\[[BufferObject]], "WebAssembly.Memory"). + 1. Let |buffer| be the result of [=create a memory buffer|creating a memory buffer=] from |memaddr|. + 1. Set |memory|.\[[BufferObject]] to |buffer|. +
+ +
+ The grow(|delta|) method, when invoked, performs the following steps: + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |memaddr| be **this**.\[[Memory]]. + 1. Let |ret| be the [=mem_size=](|store|, |memaddr|). + 1. Let |store| be [=mem_grow=](|store|, |memaddr|, |delta|). + 1. If |store| is [=error=], throw a {{RangeError}} exception. + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. [=Reset the memory buffer=] of |memaddr|. + 1. Return |ret|. +
+ +Immediately after a WebAssembly [=memory.grow=] instruction executes, perform the following steps: + +
+ 1. If the top of the stack is not [=i32.const=] (−1), + 1. Let |frame| be the [=current frame=]. + 1. Assert: due to validation, |frame|.[=frame/module=].[=moduleinst/memaddrs=][0] exists. + 1. Let |memaddr| be the memory address |frame|.[=frame/module=].[=moduleinst/memaddrs=][0]. + 1. [=Reset the memory buffer=] of |memaddr|. +
+ +
+ The getter of the buffer attribute of {{Memory}} returns **this**.\[[BufferObject]]. +
+ +

Tables

+ +
+enum TableKind {
+  "externref",
+  "anyfunc",
+  // Note: More values may be added in future iterations,
+  // e.g., typed function references, typed GC references
+};
+
+dictionary TableDescriptor {
+  required TableKind element;
+  required [EnforceRange] unsigned long initial;
+  [EnforceRange] unsigned long maximum;
+};
+
+[LegacyNamespace=WebAssembly, Exposed=*]
+interface Table {
+  constructor(TableDescriptor descriptor, optional any value);
+  unsigned long grow([EnforceRange] unsigned long delta, optional any value);
+  any get([EnforceRange] unsigned long index);
+  undefined set([EnforceRange] unsigned long index, optional any value);
+  readonly attribute unsigned long length;
+};
+
+ +A {{Table}} object represents a single [=table instance=] which can be simultaneously referenced by +multiple {{Instance}} objects. +Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address=]. + +
+ To initialize a table object |table| from a [=table address=] |tableaddr|, perform the following steps: + 1. Let |map| be the [=surrounding agent=]'s associated [=Table object cache=]. + 1. Assert: |map|[|tableaddr|] doesn't [=map/exist=]. + 1. Set |table|.\[[Table]] to |tableaddr|. + 1. [=map/Set=] |map|[|tableaddr|] to |table|. +
+ +
+ To create a table object from a [=table address=] |tableaddr|, perform the following steps: + 1. Let |map| be the [=surrounding agent=]'s associated [=Table object cache=]. + 1. If |map|[|tableaddr|] [=map/exists=], + 1. Return |map|[|tableaddr|]. + 1. Let |table| be a [=/new=] {{Table}}. + 1. [=initialize a table object|Initialize=] |table| from |tableaddr|. + 1. Return |table|. +
+ +
+ The Table(|descriptor|, |value|) constructor, when invoked, performs the following steps: + 1. Let |elementType| be [=ToValueType=](|descriptor|["element"]). + 1. If |elementType| is not a [=reftype=], + 1. [=Throw=] a {{TypeError}} exception. + 1. Let |initial| be |descriptor|["initial"]. + 1. If |descriptor|["maximum"] [=map/exists=], let |maximum| be |descriptor|["maximum"]; otherwise, let |maximum| be empty. + 1. If |maximum| is not empty and |maximum| < |initial|, throw a {{RangeError}} exception. + 1. If |value| is missing, + 1. Let |ref| be [=DefaultValue=](|elementType|). + 1. Otherwise, + 1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementType|). + 1. Let |type| be the [=table type=] {[=table type|min=] |initial|, [=table type|max=] |maximum|} |elementType|. + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let (|store|, |tableaddr|) be [=table_alloc=](|store|, |type|, |ref|). + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. [=initialize a table object|Initialize=] **this** from |tableaddr|. +
+ +
+ The grow(|delta|, |value|) method, when invoked, performs the following steps: + 1. Let |tableaddr| be **this**.\[[Table]]. + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |initialSize| be [=table_size=](|store|, |tableaddr|). + 1. Let (limits, |elementType|) be [=table_type=](|tableaddr|). + 1. If |value| is missing, + 1. Let |ref| be [=DefaultValue=](|elementType|). + 1. Otherwise, + 1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementType|). + 1. Let |result| be [=table_grow=](|store|, |tableaddr|, |delta|, |ref|). + 1. If |result| is [=error=], throw a {{RangeError}} exception. + + Note: The above exception can happen due to either insufficient memory or an invalid size parameter. + + 1. Set the [=surrounding agent=]'s [=associated store=] to |result|. + 1. Return |initialSize|. +
+ +
+ The getter of the length attribute of {{Table}}, when invoked, performs the following steps: + 1. Let |tableaddr| be **this**.\[[Table]]. + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Return [=table_size=](|store|, |tableaddr|). +
+ +
+ The get(|index|) method, when invoked, performs the following steps: + 1. Let |tableaddr| be **this**.\[[Table]]. + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |result| be [=table_read=](|store|, |tableaddr|, |index|). + 1. If |result| is [=error=], throw a {{RangeError}} exception. + 1. Return [=ToJSValue=](|result|). +
+ +
+ The set(|index|, |value|) method, when invoked, performs the following steps: + 1. Let |tableaddr| be **this**.\[[Table]]. + 1. Let (limits, |elementType|) be [=table_type=](|tableaddr|). + 1. If |value| is missing, + 1. Let |ref| be [=DefaultValue=](|elementType|). + 1. Otherwise, + 1. Let |ref| be [=?=] [=ToWebAssemblyValue=](|value|, |elementType|). + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |store| be [=table_write=](|store|, |tableaddr|, |index|, |ref|). + 1. If |store| is [=error=], throw a {{RangeError}} exception. + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. +
+ +

Globals

+ +
+enum ValueType {
+  "i32",
+  "i64",
+  "f32",
+  "f64",
+  "v128",
+  "externref",
+  "anyfunc",
+};
+
+ +Note: this type may be extended with additional cases in future versions of WebAssembly. + +
+dictionary GlobalDescriptor {
+  required ValueType value;
+  boolean mutable = false;
+};
+
+[LegacyNamespace=WebAssembly, Exposed=*]
+interface Global {
+  constructor(GlobalDescriptor descriptor, optional any v);
+  any valueOf();
+  attribute any value;
+};
+
+ +A {{Global}} object represents a single [=global instance=] +which can be simultaneously referenced by multiple {{Instance}} objects. Each +{{Global}} object has one internal slot: + + * \[[Global]] : a [=global address=] + +
+ To initialize a global object |global| from a [=global address=] |globaladdr|, perform the following steps: + 1. Let |map| be the [=surrounding agent=]'s associated [=Global object cache=]. + 1. Assert: |map|[|globaladdr|] doesn't [=map/exist=]. + 1. Set |global|.\[[Global]] to |globaladdr|. + 1. [=map/Set=] |map|[|globaladdr|] to |global|. +
+ +
+ To create a global object from a [=global address=] |globaladdr|, perform the following steps: + 1. Let |map| be the current [=agent=]'s associated [=Global object cache=]. + 1. If |map|[|globaladdr|] [=map/exists=], + 1. Return |map|[|globaladdr|]. + 1. Let |global| be a [=/new=] {{Global}}. + 1. [=initialize a global object|Initialize=] |global| from |globaladdr|. + 1. Return |global|. +
+ +
+ The algorithm ToValueType(|s|) performs the following steps: + 1. If |s| equals "i32", return [=i32=]. + 1. If |s| equals "i64", return [=i64=]. + 1. If |s| equals "f32", return [=f32=]. + 1. If |s| equals "f64", return [=f64=]. + 1. If |s| equals "v128", return [=v128=]. + 1. If |s| equals "anyfunc", return [=funcref=]. + 1. If |s| equals "externref", return [=externref=]. + 1. Assert: This step is not reached. +
+ +
+ The algorithm DefaultValue(|valuetype|) performs the following steps: + 1. If |valuetype| equals [=i32=], return [=i32.const=] 0. + 1. If |valuetype| equals [=i64=], return [=i64.const=] 0. + 1. If |valuetype| equals [=f32=], return [=f32.const=] 0. + 1. If |valuetype| equals [=f64=], return [=f64.const=] 0. + 1. If |valuetype| equals [=funcref=], return [=ref.null=] [=funcref=]. + 1. If |valuetype| equals [=externref=], return [=ToWebAssemblyValue=](undefined, |valuetype|). + 1. Assert: This step is not reached. +
+ +
+ The Global(|descriptor|, |v|) constructor, when invoked, performs the following steps: + 1. Let |mutable| be |descriptor|["mutable"]. + 1. Let |valuetype| be [=ToValueType=](|descriptor|["value"]). + 1. If |valuetype| is [=v128=], + 1. Throw a {{TypeError}} exception. + 1. If |v| is missing, + 1. Let |value| be [=DefaultValue=](|valuetype|). + 1. Otherwise, + 1. Let |value| be [=ToWebAssemblyValue=](|v|, |valuetype|). + 1. If |mutable| is true, let |globaltype| be [=var=] |valuetype|; otherwise, let |globaltype| be [=const=] |valuetype|. + 1. Let |store| be the current agent's [=associated store=]. + 1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, |globaltype|, |value|). + 1. Set the current agent's [=associated store=] to |store|. + 1. [=initialize a global object|Initialize=] **this** from |globaladdr|. +
+ +
+ The algorithm GetGlobalValue({{Global}} |global|) performs the following steps: + 1. Let |store| be the current agent's [=associated store=]. + 1. Let |globaladdr| be |global|.\[[Global]]. + 1. Let |globaltype| be [=global_type=](|store|, |globaladdr|). + 1. If |globaltype| is of the form mut [=v128=], throw a {{TypeError}}. + 1. Let |value| be [=global_read=](|store|, |globaladdr|). + 1. Return [=ToJSValue=](|value|). +
+ +
+ The getter of the value attribute of {{Global}}, when invoked, performs the following steps: + 1. Return [=GetGlobalValue=](**this**). + + The setter of the value attribute of {{Global}}, when invoked, performs the following steps: + 1. Let |store| be the current agent's [=associated store=]. + 1. Let |globaladdr| be **this**.\[[Global]]. + 1. Let |mut| |valuetype| be [=global_type=](|store|, |globaladdr|). + 1. If |valuetype| is [=v128=], throw a {{TypeError}}. + 1. If |mut| is [=const=], throw a {{TypeError}}. + 1. Let |value| be [=ToWebAssemblyValue=](**the given value**, |valuetype|). + 1. Let |store| be [=global_write=](|store|, |globaladdr|, |value|). + 1. If |store| is [=error=], throw a {{RangeError}} exception. + 1. Set the current agent's [=associated store=] to |store|. +
+ +
+ The valueOf() method, when invoked, performs the following steps: + 1. Return [=GetGlobalValue=](**this**). +
+ +

Exported Functions

+ +A WebAssembly function is made available in JavaScript as an Exported Function. +Exported Functions are [=Built-in Function Objects=] which are not constructors, and which have a \[[FunctionAddress]] internal slot. +This slot holds a [=function address=] relative to the [=surrounding agent=]'s [=associated store=]. + +
+ The name of the WebAssembly function |funcaddr| is found by performing the following steps: + + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |funcinst| be |store|.funcs[|funcaddr|]. + 1. If |funcinst| is of the form {type functype, hostcode |hostfunc|}, + 1. Assert: |hostfunc| is a JavaScript object and [$IsCallable$](|hostfunc|) is true. + 1. Let |index| be the [=index of the host function=] |funcaddr|. + 1. Otherwise, + 1. Let |moduleinst| be |funcinst|.module. + 1. Assert: |funcaddr| is contained in |moduleinst|.funcaddrs. + 1. Let |index| be the index of |moduleinst|.funcaddrs where |funcaddr| is found. + 1. Return [=!=] [$ToString$](|index|). +
+ +
+ To create a new Exported Function from a WebAssembly [=function address=] |funcaddr|, perform the following steps: + + 1. Let |map| be the [=surrounding agent=]'s associated [=Exported Function cache=]. + 1. If |map|[|funcaddr|] [=map/exists=], + 1. Return |map|[|funcaddr|]. + 1. Let |steps| be "[=call an Exported Function|call the Exported Function=] |funcaddr| with arguments." + 1. Let |realm| be the [=current Realm=]. + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |functype| be [=func_type=](|store|, |funcaddr|). + 1. Let [|paramTypes|] → [resultTypes] be |functype|. + 1. Let |arity| be |paramTypes|'s [=list/size=]. + 1. Let |name| be the [=name of the WebAssembly function=] |funcaddr|. + 1. Let |function| be [=!=] [$CreateBuiltinFunction$](|steps|, |arity|, |name|, « \[[FunctionAddress]] », |realm|). + 1. Set |function|.\[[FunctionAddress]] to |funcaddr|. + 1. [=map/Set=] |map|[|funcaddr|] to |function|. + 1. Return |function|. +
+ +
+ To call an Exported Function with [=function address=] |funcaddr| and a [=list=] of JavaScript arguments |argValues|, perform the following steps: + + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |functype| be [=func_type=](|store|, |funcaddr|). + 1. Let [|parameters|] → [|results|] be |functype|. + 1. If |parameters| or |results| contain [=v128=], throw a {{TypeError}}. + + Note: the above error is thrown each time the \[[Call]] method is invoked. + 1. Let |args| be « ». + 1. Let |i| be 0. + 1. [=list/iterate|For each=] |t| of |parameters|, + 1. If |argValues|'s [=list/size=] > |i|, let |arg| be |argValues|[|i|]. + 1. Otherwise, let |arg| be undefined. + 1. [=list/Append=] [=ToWebAssemblyValue=](|arg|, |t|) to |args|. + 1. Set |i| to |i| + 1. + 1. Let (|store|, |ret|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). + 1. Note: The expectation is that [=func_invoke=] will be updated to return (|store|, val* | [=error=] | (exception |exntag| |payload| |opaqueData|)). + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. If |ret| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. + 1. If |ret| is exception |exntag| |payload| |opaqueData|, then + 1. If |opaqueData| is not [=ref.null=] [=externref=], + 1. Let « [=ref.extern=] |externaddr| » be |opaqueData|. + 1. Throw the result of [=retrieving an extern value=] from |externaddr|. + 1. Let |exception| be [=create an Exception object|a new Exception=] for |exntag| and |payload|. + 1. Throw |exception|. + 1. Let |outArity| be the [=list/size=] of |ret|. + 1. If |outArity| is 0, return undefined. + 1. Otherwise, if |outArity| is 1, return [=ToJSValue=](|ret|[0]). + 1. Otherwise, + 1. Let |values| be « ». + 1. [=list/iterate|For each=] |r| of |ret|, + 1. [=list/Append=] [=ToJSValue=](|r|) to |values|. + 1. Return [$CreateArrayFromList$](|values|). +
+ +Note: [=call an Exported Function|Calling an Exported Function=] executes in the \[[Realm]] of the callee Exported Function, as per the definition of [=built-in function objects=]. + +Note: Exported Functions do not have a \[[Construct]] method and thus it is not possible to call one with the `new` operator. + +
+ To run a host function from the JavaScript object |func|, type |functype|, and [=list=] of [=WebAssembly values=] |arguments|, perform the following steps: + + 1. Let [|parameters|] → [|results|] be |functype|. + 1. If |parameters| or |results| contain [=v128=], throw a {{TypeError}}. + 1. Let |jsArguments| be « ». + 1. [=list/iterate|For each=] |arg| of |arguments|, + 1. [=list/Append=] [=!=] [=ToJSValue=](|arg|) to |jsArguments|. + 1. Let |ret| be [=?=] [$Call$](|func|, undefined, |jsArguments|). + 1. Let |resultsSize| be |results|'s [=list/size=]. + 1. If |resultsSize| is 0, return « ». + 1. Otherwise, if |resultsSize| is 1, return « [=?=] [=ToWebAssemblyValue=](|ret|, |results|[0]) ». + 1. Otherwise, + 1. Let |method| be [=?=] [$GetMethod$](|ret|, {{@@iterator}}). + 1. If |method| is undefined, [=throw=] a {{TypeError}}. + 1. Let |values| be [=?=] [$IterableToList$](|ret|, |method|). + 1. Let |wasmValues| be a new, empty [=list=]. + 1. If |values|'s [=list/size=] is not |resultsSize|, throw a {{TypeError}} exception. + 1. For each |value| and |resultType| in |values| and |results|, paired linearly, + 1. [=list/Append=] [=ToWebAssemblyValue=](|value|, |resultType|) to |wasmValues|. + 1. Return |wasmValues|. +
+ +
+ To create a host function from the JavaScript object |func| and type |functype|, perform the following steps: + + 1. Assert: [$IsCallable$](|func|). + 1. Let |stored settings| be the incumbent settings object. + 1. Let |hostfunc| be a [=host function=] which performs the following steps when called with arguments |arguments|: + 1. Let |realm| be |func|'s [=associated Realm=]. + 1. Let |relevant settings| be |realm|'s [=realm/settings object=]. + 1. [=Prepare to run script=] with |relevant settings|. + 1. [=Prepare to run a callback=] with |stored settings|. + 1. Let |result| be the result of [=run a host function|running a host function=] from |func|, |functype|, and |arguments|. + 1. [=Clean up after running a callback=] with |stored settings|. + 1. [=Clean up after running script=] with |relevant settings|. + 1. Assert: |result|.\[[Type]] is throw or normal. + 1. If |result|.\[[Type]] is throw, then: + 1. Let |v| be |result|.\[[Value]]. + 1. If |v| [=implements=] {{Exception}}, + 1. Let |type| be |v|.\[[Type]]. + 1. Let |payload| be |v|.\[[Payload]]. + 1. Otherwise, + 1. Let |type| be the [=JavaScript exception tag=]. + 1. Let |payload| be « ». + 1. Let |opaqueData| be [=ToWebAssemblyValue=](|v|, [=externref=]) + 1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. + 1. Otherwise, return |result|.\[[Value]]. + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let (|store|, |funcaddr|) be [=func_alloc=](|store|, |functype|, |hostfunc|). + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. Return |funcaddr|. +
+ +
+The algorithm ToJSValue(|w|) coerces a [=WebAssembly value=] to a JavaScript value by performing the following steps: + +1. Assert: |w| is not of the form [=v128.const=] v128. +1. If |w| is of the form [=i64.const=] |i64|, + 1. Let |v| be [=signed_64=](|i64|). + 1. Return [=ℤ=](|v| interpreted as a mathematical value). +1. If |w| is of the form [=i32.const=] |i32|, return [=𝔽=]([=signed_32=](|i32| interpreted as a mathematical value)). +1. If |w| is of the form [=f32.const=] |f32|, + 1. If |f32| is [=+∞=] or [=−∞=], return **+∞**𝔽 or **-∞**𝔽, respectively. + 1. If |f32| is [=nan=], return **NaN**. + 1. Return [=𝔽=](|f32| interpreted as a mathematical value). +1. If |w| is of the form [=f64.const=] |f64|, + 1. If |f64| is [=+∞=] or [=−∞=], return **+∞**𝔽 or **-∞**𝔽, respectively. + 1. If |f64| is [=nan=], return **NaN**. + 1. Return [=𝔽=](|f64| interpreted as a mathematical value). +1. If |w| is of the form [=ref.null=] t, return null. +1. If |w| is of the form [=ref.func=] |funcaddr|, return the result of creating [=a new Exported Function=] from |funcaddr|. +1. If |w| is of the form [=ref.extern=] |externaddr|, return the result of [=retrieving an extern value=] from |externaddr|. + +Note: Number values which are equal to NaN may have various observable NaN payloads; see [$NumericToRawBytes$] for details. +
+ +
+ +For retrieving an extern value from an [=extern address=] |externaddr|, perform the following steps: + +1. Let |map| be the [=surrounding agent=]'s associated [=extern value cache=]. +1. Assert: |map|[|externaddr|] [=map/exists=]. +1. Return |map|[|externaddr|]. + +
+ +
+The algorithm ToWebAssemblyValue(|v|, |type|) coerces a JavaScript value to a [=WebAssembly value=] by performing the following steps: + +1. Assert: |type| is not [=v128=]. +1. If |type| is [=i64=], + 1. Let |i64| be [=?=] [$ToBigInt64$](|v|). + 1. Return [=i64.const=] |i64|. +1. If |type| is [=i32=], + 1. Let |i32| be [=?=] [$ToInt32$](|v|). + 1. Return [=i32.const=] |i32|. +1. If |type| is [=f32=], + 1. Let |number| be [=?=] [$ToNumber$](|v|). + 1. If |number| is **NaN**, + 1. Let |n| be an implementation-defined integer such that [=canon=]32 ≤ |n| < 2[=signif=](32). + 1. Let |f32| be [=nan=](n). + 1. Otherwise, + 1. Let |f32| be |number| rounded to the nearest representable value using IEEE 754-2008 round to nearest, ties to even mode. [[IEEE-754]] + 1. Return [=f32.const=] |f32|. +1. If |type| is [=f64=], + 1. Let |number| be [=?=] [$ToNumber$](|v|). + 1. If |number| is **NaN**, + 1. Let |n| be an implementation-defined integer such that [=canon=]64 ≤ |n| < 2[=signif=](64). + 1. Let |f64| be [=nan=](n). + 1. Otherwise, + 1. Let |f64| be |number|. + 1. Return [=f64.const=] |f64|. +1. If |type| is [=funcref=], + 1. If |v| is null, + 1. Return [=ref.null=] [=funcref=]. + 1. If |v| is an [=Exported Function=], + 1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot. + 1. Return [=ref.func=] |funcaddr|. + 1. Throw a {{TypeError}}. +1. If |type| is [=externref=], + 1. If |v| is null, + 1. Return [=ref.null=] [=externref=]. + 1. Let |map| be the [=surrounding agent=]'s associated [=extern value cache=]. + 1. If a [=extern address=] |externaddr| exists such that |map|[|externaddr|] is the same as |v|, + 1. Return [=ref.extern=] |externaddr|. + 1. Let [=extern address=] |externaddr| be the smallest address such that |map|[|externaddr|] [=map/exists=] is false. + 1. [=map/Set=] |map|[|externaddr|] to |v|. + 1. Return [=ref.extern=] |externaddr|. +1. Assert: This step is not reached. + +
+ +

Tags

+ +The tag_alloc(|store|, |parameters|) algorithm creates a new [=tag address=] for |parameters| in |store| and returns the updated store and the [=tag address=]. + +The tag_parameters(|store|, |tagAddress|) algorithm returns the [=list=] of types for |tagAddress| in |store|. + +

Exception types

+ +
+dictionary TagType {
+  required sequence<ValueType> parameters;
+};
+
+[LegacyNamespace=WebAssembly, Exposed=(Window,Worker,Worklet)]
+interface Tag {
+  constructor(TagType type);
+  TagType type();
+};
+
+ +A {{Tag}} value represents a type of exception. + +
+ +To initialize a Tag object |tag| from a [=tag address=] |tagAddress|, perform the following steps: + +1. Let |map| be the [=surrounding agent=]'s associated [=Tag object cache=]. +1. Assert: |map|[|tagAddress|] doesn't [=map/exist=]. +1. Set |tag|.\[[Address]] to |tagAddress|. +1. [=map/Set=] |map|[|tagAddress|] to |tag|. + +
+ +
+ +To create a Tag object from a [=tag address=] |tagAddress|, perform the following steps: + +1. Let |map| be the [=surrounding agent=]'s associated [=Tag object cache=]. +1. If |map|[|tagAddress|] [=map/exists=], + 1. Return |map|[|tagAddress|]. +1. Let |tag| be a [=new=] {{Tag}}. +1. [=initialize a Tag object|Initialize=] |tag| from |tagAddress|. +1. Return |tag|. + +
+ +
+ +The new Tag(|type|) constructor steps are: + +1. Let |parameters| be |type|["parameters"]. +1. Let |wasmParameters| be «». +1. [=list/iterate|For each=] |type| of |parameters|, + 1. [=list/Append=] [=ToValueType=](|type|) to |wasmParameters|. +1. Let |store| be the current agent's [=associated store=]. +1. Let (|store|, |tagAddress|) be [=tag_alloc=](|store|, |wasmParameters|). +1. Set the current agent's [=associated store=] to |store|. +1. [=initialize a Tag object|Initialize=] **this** from |tagAddress|. + +
+ +
+ +The type() method steps are: + +1. Let |store| be the [=surrounding agent=]'s [=associated store=]. +1. Let |parameters| be [=tag_parameters=](|store|, **this**.\[[Address]]). +1. Let |idlParameters| be «». +1. [=list/iterate|For each=] |type| of |parameters|, + 1. [=list/Append=] [$FromValueType$](|type|) to |idlParameters|. +1. Return «[ "{{TagType/parameters}}" → |idlParameters| ]». + +Advisement: This method is only expected to be implemented or shipped when both this proposal and the Type Reflection proposal are implemented or shipped (respectively). + +
+ +

Runtime exceptions

+ +
+dictionary ExceptionOptions {
+  boolean traceStack = false;
+};
+
+[LegacyNamespace=WebAssembly, Exposed=(Window,Worker,Worklet)]
+interface Exception {
+  constructor(Tag exceptionTag, sequence<any> payload, optional ExceptionOptions options = {});
+  any getArg(Tag exceptionTag, [EnforceRange] unsigned long index);
+  boolean is(Tag exceptionTag);
+  readonly attribute (DOMString or undefined) stack;
+};
+
+ +An {{Exception}} value represents an exception. + +
+ +To create an Exception object from a [=tag address=] |tagAddress| and a [=list=] of +WebAssembly values |payload|, perform the following steps: + +1. Let |store| be the [=surrounding agent=]'s [=associated store=]. +1. Let |types| be [=tag_parameters=](|store|, |tagAddress|). +1. Assert: |types|'s [=list/size=] is |payload|'s [=list/size=]. +1. [=list/iterate|For each=] |value| and |resultType| of |payload| and |types|, paired linearly, + 1. Assert: |value|'s type matches |resultType|. +1. Let |exception| be a [=new=] {{Exception}}. +1. Set |exception|.\[[Type]] to |tagAddress|. +1. Set |exception|.\[[Payload]] to |payload|. +1. Set |exception|.\[[Stack]] to undefined. +1. Return |exception|. + +
+ +
+ +The new Exception(|exceptionTag|, |payload|, |options|) +constructor steps are: + +1. Let |store| be the [=surrounding agent=]'s [=associated store=]. +1. Let |types| be [=tag_parameters=](|store|, |exceptionTag|.\[[Address]]). +1. If |types|'s [=list/size=] is not |payload|'s [=list/size=], + 1. Throw a {{TypeError}}. +1. Let |wasmPayload| be « ». +1. [=list/iterate|For each=] |value| and |resultType| of |payload| and |types|, paired linearly, + 1. [=list/Append=] ? [=ToWebAssemblyValue=](|value|, |resultType|) to |wasmPayload|. +1. Set **this**.\[[Type]] to |exceptionTag|.\[[Address]]. +1. Set **this**.\[[Payload]] to |wasmPayload|. +1. If |options|["traceStack"] is true, + 1. Set **this**.\[[Stack]] to either a {{DOMString}} representation of the current call stack or undefined. +1. Otherwise, + 1. Set **this**.\[[Stack]] to undefined. + +
+ +
+ +The getArg(|exceptionTag|, |index|) method steps are: + +1. If **this**.\[[Type]] is not equal to |exceptionTag|.\[[Address]], + 1. Throw a {{TypeError}}. +1. Let |payload| be **this**.\[[Payload]]. +1. If |index| ≥ |payload|'s [=list/size=], + 1. Throw a {{RangeError}}. +1. Return [=ToJSValue=](|payload|[|index|]). + +
+ +
+ +The is(|exceptionTag|) method steps are: + +1. If **this**.\[[Type]] is not equal to |exceptionTag|.\[[Address]], + 1. Return false. +1. Return true. + +
+ +
+ +The stack getter steps are: + +1. Return **this**.\[[Stack]]. + +
+ +

JavaScript exceptions

+ +The JavaScript exception tag is a [=tag address=] reserved by this +specification to distinguish exceptions originating from JavaScript. + +For any [=associated store=] |store|, the result of +[=tag_parameters=](|store|, [=JavaScript exception tag=]) must be « ». + +
+ +To throw with a [=tag address=] |type|, a matching [=list=] of WebAssembly values |payload|, and an [=externref=] |opaqueData|, perform the following steps: + +1. Unwind the stack until reaching the *catching try block* given |type|. +1. Invoke the catch block with |payload| and |opaqueData|. + +Note: This algorithm is expected to be moved into the core specification. + +
+ +

Error Objects

+ +WebAssembly defines the following Error classes: CompileError, LinkError, and RuntimeError. + +
+When the [=namespace object=] for the {{WebAssembly}} namespace is [=create a namespace object|created=], the following steps must be run: + +1. Let |namespaceObject| be the [=namespace object=]. +1. [=list/iterate|For each=] |error| of « "CompileError", "LinkError", "RuntimeError" », + 1. Let |constructor| be a new object, implementing the [=NativeError Object Structure=], with NativeError set to |error|. + 1. [=!=] [$CreateMethodProperty$](|namespaceObject|, |error|, |constructor|). + +
+ +Note: This defines {{CompileError}}, {{LinkError}}, and {{RuntimeError}} classes on the {{WebAssembly}} namespace, which are produced by the APIs defined in this specification. +They expose the same interface as native JavaScript errors like {{TypeError}} and {{RangeError}}. + +Note: It is not currently possible to define this behavior using Web IDL. + + +

Error Condition Mappings to JavaScript

+ +Running WebAssembly programs encounter certain events which halt execution of the WebAssembly code. +WebAssembly code (currently) +has no way to catch these conditions and thus an exception will necessarily +propagate to the enclosing non-WebAssembly caller (whether it is a browser, +JavaScript or another runtime system) where it is handled like a normal JavaScript exception. + +If WebAssembly calls JavaScript via import and the JavaScript throws an +exception, the exception is propagated through the WebAssembly activation to the +enclosing caller. + +Because JavaScript exceptions can be handled, and JavaScript can continue to +call WebAssembly exports after a trap has been handled, traps do not, in +general, prevent future execution. + +

Stack Overflow

+ +Whenever a stack overflow occurs in +WebAssembly code, the same class of exception is thrown as for a stack overflow in +JavaScript. The particular exception here is implementation-defined in both cases. + +Note: ECMAScript doesn't specify any sort of behavior on stack overflow; implementations have been observed to throw {{RangeError}}, InternalError or Error. Any is valid here. + +

Out of Memory

+ +Whenever validation, compilation or instantiation run out of memory, the +same class of exception is thrown as for out of memory conditions in JavaScript. +The particular exception here is implementation-defined in both cases. + +Note: ECMAScript doesn't specify any sort of behavior on out-of-memory conditions; implementations have been observed to throw OOMError and to crash. Either is valid here. + +
+ A failed allocation of a large table or memory may either result in + - a {{RangeError}}, as specified in the {{Memory}} {{Memory/grow()}} and {{Table}} {{Table/grow()}} operations + - returning -1 as the [=memory.grow=] instruction + - UA-specific OOM behavior as described in this section. + In a future revision, we may reconsider more reliable and recoverable errors for allocations of large amounts of memory. + + See [Issue 879](https://github.com/WebAssembly/spec/issues/879) for further discussion. +
+ +

Implementation-defined Limits

+ +The WebAssembly core specification allows an implementation to define limits on the syntactic structure of the module. +While each embedding of WebAssembly may choose to define its own limits, for predictability the standard WebAssembly JavaScript Interface described in this document defines the following exact limits. +An implementation must reject a module that exceeds one of the following limits with a {{CompileError}}: +In practice, an implementation may run out of resources for valid modules below these limits. + +
    +
  • The maximum size of a module is 1,073,741,824 bytes (1 GiB).
  • +
  • The maximum number of types defined in the types section is 1,000,000.
  • +
  • The maximum number of functions defined in a module is 1,000,000.
  • +
  • The maximum number of imports declared in a module is 100,000.
  • +
  • The maximum number of exports declared in a module is 100,000.
  • +
  • The maximum number of globals defined in a module is 1,000,000.
  • +
  • The maximum number of tags defined in a module is 1,000,000.
  • +
  • The maximum number of data segments defined in a module is 100,000.
  • + +
  • The maximum number of tables, including declared or imported tables, is 100,000.
  • +
  • The maximum size of a table is 10,000,000.
  • +
  • The maximum number of table entries in any table initialization is 10,000,000.
  • +
  • The maximum number of memories, including declared or imported memories, is 1.
  • + +
  • The maximum number of parameters to any function or block is 1,000.
  • +
  • The maximum number of return values for any function or block is 1,000.
  • +
  • The maximum size of a function body, including locals declarations, is 7,654,321 bytes.
  • +
  • The maximum number of locals declared in a function, including implicitly declared as parameters, is 50,000.
  • +
+ +An implementation must throw a {{RuntimeError}} if one of the following limits is exceeded during runtime: +In practice, an implementation may run out of resources for valid modules below these limits. + +
    +
  • The maximum size of a table is 10,000,000.
  • +
  • The maximum number of pages of a memory is 65,536.
  • +
+ +

Security and Privacy Considerations

+ +

This section is non-normative.

+ +This document defines a host environment for WebAssembly. It enables a WebAssembly instance to [=import=] JavaScript objects and functions from an [=read the imports|import object=], but otherwise provides no access to the embedding environment. Thus a WebAssembly instance is bound to the same constraints as JavaScript. diff --git a/document/metadata/code/.gitignore b/document/metadata/code/.gitignore new file mode 100644 index 000000000..b932ec283 --- /dev/null +++ b/document/metadata/code/.gitignore @@ -0,0 +1,3 @@ +_build +_static +document/*.pyc diff --git a/document/metadata/code/LICENSE b/document/metadata/code/LICENSE new file mode 100644 index 000000000..795b406e4 --- /dev/null +++ b/document/metadata/code/LICENSE @@ -0,0 +1,50 @@ +W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE + +This work is being provided by the copyright holders under the following +license. + + +LICENSE + +By obtaining and/or copying this work, you (the licensee) agree that you have +read, understood, and will comply with the following terms and conditions. + +Permission to copy, modify, and distribute this work, with or without +modification, for any purpose and without fee or royalty is hereby granted, +provided that you include the following on ALL copies of the work or portions +thereof, including modifications: + +* The full text of this NOTICE in a location viewable to users of the + redistributed or derivative work. + +* Any pre-existing intellectual property disclaimers, notices, or terms and + conditions. If none exist, the W3C Software and Document Short Notice + (https://www.w3.org/Consortium/Legal/copyright-software-short-notice) should + be included. + +* Notice of any changes or modifications, through a copyright statement on the + new code or document such as "This software or document includes material + copied from or derived from [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." + + +DISCLAIMERS + +THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS +OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF +MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE +SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, +TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. + +The name and trademarks of copyright holders may NOT be used in advertising or +publicity pertaining to the work without specific, written prior permission. +Title to copyright in this work will at all times remain with copyright +holders. + + +NOTES + +This version: +http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document diff --git a/document/metadata/code/Makefile b/document/metadata/code/Makefile new file mode 100644 index 000000000..e8b975c72 --- /dev/null +++ b/document/metadata/code/Makefile @@ -0,0 +1,361 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = a4 +BUILDDIR = _build +STATICDIR = _static +DOWNLOADDIR = _download +NAME = WebAssembly-Metadata-Code + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: usage +usage: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " pdf to make standalone PDF file" + @echo " bikeshed to make a bikeshed wrapped single large HTML file" + @echo " diff to make a diff of the bikeshed HTML file with the latest TR" + @echo " WD-tar generate tar file for updating the Working Draft" + @echo " WD-echidna publish the Working Draft tar file via Echidna" + @echo " all to make all 3" + @echo " publish to make all and push to gh-pages" + @echo " help to see more options" + +.PHONY: help +help: + @echo "Usage: \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " pdf to make standalone PDF file" + @echo " bikeshed to make a bikeshed wrapped single large HTML file" + @echo " all to make all 3" + @echo " publish to make all and push to gh-pages" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " epub3 to make an epub3" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + @echo " dummy to check syntax errors of document sources" + +.PHONY: deploy +deploy: + (cd ../..; make dir-core deploy-core) + +.PHONY: publish +publish: clean all deploy + +.PHONY: publish-main +publish-main: clean main deploy + +.PHONY: all +all: pdf html + +.PHONY: main +main: pdf html + +# Dirty hack to avoid rebuilding the Bikeshed version for every push. +.PHONY: bikeshed-keep +bikeshed-keep: + test -e $(BUILDDIR)/html/bikeshed || \ + wget -r -nH --cut-dirs=2 -P $(BUILDDIR)/html --no-check-certificate \ + https://webassembly.github.io/spec/core/bikeshed || \ + echo Downloaded Bikeshed. + + +%.rst: %.py + (cd `dirname $@`; ./`basename $^`) + +.PHONY: pdf +pdf: latexpdf + mkdir -p $(BUILDDIR)/html/$(DOWNLOADDIR) + ln -f $(BUILDDIR)/latex/$(NAME).pdf $(BUILDDIR)/html/$(DOWNLOADDIR)/$(NAME).pdf + + +.PHONY: clean +clean: + rm -rf $(BUILDDIR) + rm -rf $(STATICDIR) + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + for file in `ls $(BUILDDIR)/html/*.html`; \ + do \ + sed s:BASEDIR:.:g <$$file >$$file.out; \ + mv -f $$file.out $$file; \ + done + for file in `ls $(BUILDDIR)/html/*/*.html`; \ + do \ + sed s:BASEDIR:..:g <$$file >$$file.out; \ + mv -f $$file.out $$file; \ + done + @echo + @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html/." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: bikeshed +bikeshed: + $(SPHINXBUILD) -b singlehtml -c util/bikeshed \ + $(ALLSPHINXOPTS) $(BUILDDIR)/bikeshed_singlehtml + python3 util/bikeshed_fixup.py $(BUILDDIR)/bikeshed_singlehtml/index.html \ + >$(BUILDDIR)/bikeshed_singlehtml/index_fixed.html + @echo ==== Showing contents of _build/bikeshed_singlehtml/index_fixed.html ==== + @head -n10 _build/bikeshed_singlehtml/index_fixed.html + @echo ... skipping $$(expr `cat _build/bikeshed_singlehtml/index_fixed.html | wc -l` - 20) lines ... + @tail -n10 _build/bikeshed_singlehtml/index_fixed.html + @echo + @echo ========================================================================= + mkdir -p $(BUILDDIR)/bikeshed_mathjax/ + bikeshed spec index.bs $(BUILDDIR)/bikeshed_mathjax/index.html + mkdir -p $(BUILDDIR)/html/bikeshed/ + (cd util/katex/ && yarn && yarn build && npm install --only=prod) + python3 util/mathjax2katex.py $(BUILDDIR)/bikeshed_mathjax/index.html \ + >$(BUILDDIR)/html/bikeshed/index.html + mkdir -p $(BUILDDIR)/html/bikeshed/katex/dist/ + cp -r util/katex/dist/* $(BUILDDIR)/html/bikeshed/katex/dist/ + patch -p0 $(BUILDDIR)/html/bikeshed/katex/dist/katex.css \ + < util/katex_fix.patch + cp $(BUILDDIR)/bikeshed_singlehtml/_static/pygments.css \ + $(BUILDDIR)/html/bikeshed/ + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/html/bikeshed/." + +.PHONY: WD-tar +WD-tar: bikeshed + @echo "Building tar file..." + tar cvf \ + $(BUILDDIR)/WD.tar \ + --transform='s|$(BUILDDIR)/html/bikeshed/||' \ + --transform='s|index.html|Overview.html|' \ + $(BUILDDIR)/html/bikeshed/index.html \ + $(BUILDDIR)/html/bikeshed/pygments.css \ + $(BUILDDIR)/html/bikeshed/katex/dist/katex.css \ + $(BUILDDIR)/html/bikeshed/katex/dist/fonts + @echo "Built $(BUILDDIR)/WD.tar." + +.PHONY: WD-echidna +WD-echidna: WD-tar + @if [ -z $(W3C_USERNAME) ] || \ + [ -z $(W3C_PASSWORD) ] || \ + [ -z $(DECISION_URL) ] ; then \ + echo "Must provide W3C_USERNAME, W3C_PASSWORD, and DECISION_URL environment variables"; \ + exit 1; \ + fi + curl 'https://labs.w3.org/echidna/api/request' \ + --user '$(W3C_USERNAME):$(W3C_PASSWORD)' \ + -F "tar=@$(BUILDDIR)/WD.tar" \ + -F "decision=$(DECISION_URL)" | tee $(BUILDDIR)/WD-echidna-id.txt + @echo + @echo "Published working draft. Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" + +.PHONY: diff +diff: bikeshed + @echo "Downloading the old single-file html spec..." + curl `grep "^TR" index.bs | cut -d' ' -f2` -o $(BUILDDIR)/html/bikeshed/old.html + @echo "Done." + @echo "Diffing new against old (go get a coffee)..." + perl ../util/htmldiff.pl $(BUILDDIR)/html/bikeshed/old.html $(BUILDDIR)/html/bikeshed/index.html $(BUILDDIR)/html/bikeshed/diff.html + @echo "Done. The diff is at $(BUILDDIR)/html/bikeshed/diff.html" + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/WebAssembly.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/WebAssembly.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/WebAssembly" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/WebAssembly" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: epub3 +epub3: + $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 + @echo + @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." + + +.PHONY: latex-core-aux +latex-core-aux: + (cd ../../core; make BUILDDIR=$(BUILDDIR) latexpdf) + mkdir -p $(BUILDDIR)/latex + cp ../../core/$(BUILDDIR)/latex/WebAssembly.aux $(BUILDDIR)/latex/ + +.PHONY: latex +latex: latex-core-aux + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: latex-core-aux + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex LATEXMKOPTS=" $(BUILDDIR)/latex/LOG 2>&1 || cat $(BUILDDIR)/latex/LOG + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: latex-core-aux + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + +.PHONY: dummy +dummy: + $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy + @echo + @echo "Build finished. Dummy builder generates no files." diff --git a/document/metadata/code/README.md b/document/metadata/code/README.md new file mode 100644 index 000000000..d5fe68ce8 --- /dev/null +++ b/document/metadata/code/README.md @@ -0,0 +1,25 @@ +# WebAssembly Code Metadata Specification + +This is the official WebAssembly "language" specification. + +It uses [Sphinx](http://www.sphinx-doc.org/). To install that: +``` +pip install sphinx +``` +To make HTML (result in `_build/html`): +``` +make html +``` +To make PDF (result in `_build/latex`, requires LaTeX): +``` +make pdf +``` +To make all: +``` +make all +``` +Finally, to make all and update webassembly.github.io/spec with it: +``` +make publish +``` +Please make sure to only use that once a change has approval. diff --git a/document/metadata/code/binary.rst b/document/metadata/code/binary.rst new file mode 100644 index 000000000..1b1726551 --- /dev/null +++ b/document/metadata/code/binary.rst @@ -0,0 +1,69 @@ +.. _binary: + +Binary Format +============= + +.. _binary-codemetadata: + +Code Metadata +------------- + +A Code Metadata item is a piece of information logically attached to an instruction. + +An item is associated with a format, which defines the item's payload. + +All code metadata items of a format named *T* are grouped under a custom section +named *'metadata.code.T'*. +The following parametrized grammar rules define the generic structure of a code metadata +section of format *T*. + +.. math:: + \begin{array}{llcll} + \production{code metadata section} & \Bcodemetadatasec(\B{T}) &::=& + \Bsection_0(\Bcodemetadata(\B{T})) \\ + \production{code metadata} & \Bcodemetadata(\B{T}) &::=& + n{:}\Bname & (\iff n = \text{metadata.code.T}) \\ &&& + \Bvec(\Bcodemetadatafunc(\B{T})) \\ + \production{code metadata function} & \Bcodemetadatafunc(\B{T}) &::=& + x{:}\Bfuncidx~\Bvec(\Bcodemetadataitem(\B{T})) \\ + \production{code metadata item} & \Bcodemetadataitem(\B{T}) &::=& + \X{off}{:}\Bu32 ~~ \X{size}{:}\Bu32 & (\iff \X{size} = ||\B{T}||) \\ &&& + \X{data}{:}\B{T} \\ + \end{array} +.. index:: ! code metadata section + +Where :math:`\X{off}` is the byte offset of the attached instruction, relative to the beginning of the |Bfunc| declaration, and :math:`\X{data}` is a further payload, whose content depends on the format :math:`T`. + +|Bcodemetadatafunc| entries must appear in order of increasing :math:`x`, and duplicate id values are not allowed. |Bcodemetadata| entries must appear in order of increasing :math:`\X{off}`, and duplicate offset values are not allowed. + +.. _binary-branchhints: + +Branch Hints +~~~~~~~~~~~~ + +A Branch Hint is a code metadata item with format *branch_hint*. + +It can only be attached to |BRIF| and |IF| instructions. + +Its payload indicates whether the branch is likely or unlikely to be taken. + +All branch hints for a module are contained in a single code metadata section +with name *'metadata.code.branch_hint'*. + +.. math:: + \begin{array}{llcll} + \production{branch hint section} & \Bbranchhintsec &::=& + \Bcodemetadatasec(\Bbranchhint) \\ + \production{branch hint} & \Bbranchhint &::=& + \Bunlikely \\ &&|& + \Blikely \\ + \production{unlikely} & \Bunlikely &::=& + \hex{00} \\ + \production{likely} & \Blikely &::=& + \hex{01} \\ + \end{array} +.. index:: ! branch hint section + +A value of |Blikely| means that the branch is likely to be taken, while a +value of |Bunlikely| means the opposite. A branch with no hints is considered +equally likely to be taken or not. diff --git a/document/metadata/code/conf.py b/document/metadata/code/conf.py new file mode 100644 index 000000000..bff6550b4 --- /dev/null +++ b/document/metadata/code/conf.py @@ -0,0 +1,505 @@ +# -*- coding: utf-8 -*- +# +# WebAssembly documentation build configuration file, created by +# sphinx-quickstart on Mon Nov 21 11:32:49 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +import os +import sys +from datetime import date + +core_dir = os.path.abspath('../../core') +sys.path.insert(0, core_dir) +pwd = os.path.abspath('.') +sys.path.insert(0, pwd) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +needs_sphinx = '2.3' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.mathjax', + 'sphinx.ext.ifconfig', + 'sphinx.ext.githubpages', + 'util.mathdef', + 'util.pseudo-lexer' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +source_suffix = ['.rst'] + +# The encoding of source files. +# +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +name = 'WebAssembly-Metadata-Code' +project = u'WebAssembly-Metadata-Code' +title = u'WebAssembly Code Metadata Specification' +copyright = u'2024, WebAssembly Community Group' +author = u'WebAssembly Community Group' +editor = u'Yuri Iozzelli (editor)' +logo = 'static/webassembly.png' + +# The name of the GitHub repository this resides in +repo = 'spec' + +# The draft version string (clear out for release cuts) +draft = ' (Draft ' + date.today().strftime("%Y-%m-%d") + ')' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '' +# The full version, including alpha/beta/rc tags. +release = version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# +# today = '' +# +# Else, today_fmt is used as the format for a strftime call. +# +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +html_theme_options = { + 'logo': logo, + 'logo_name': 'WebAssembly', + 'description': 'WebAssembly Specification', + 'fixed_sidebar': True, + 'sidebar_width': '260px', + 'sidebar_collapse': True, + 'show_powered_by': False, + 'extra_nav_links': { + 'Download as PDF': 'BASEDIR/_download/' + name + '.pdf' + }, +} + +html_sidebars = { + '**': [ + # 'about.html', + 'navigation.html', + # 'relations.html', + 'searchbox.html', + ] +} + + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. +# " v documentation" by default. +# +html_title = u'WebAssembly Custom Sections' + +# A shorter title for the navigation bar. Default is the same as html_title. +# +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# +html_logo = logo + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['static/custom.css'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# +# html_extra_path = [] + +# If not None, a 'Last updated on:' timestamp is inserted at every page +# bottom, using the given strftime format. +# The empty string is equivalent to '%b %d, %Y'. +# +# html_last_updated_fmt = None + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# +# html_use_smartypants = True + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# +# html_additional_pages = {} + +# If false, no module index is generated. +# +html_domain_indices = False + +# If false, no index is generated. +# +html_use_index = False + +# If true, the index is split into individual pages for each letter. +# +html_split_index = False + +# If true, the reST sources are included in the HTML build as _sources/name. The default is True. +# +html_copy_source = False + +# If true, links to the reST sources are added to the pages. +# +html_show_sourcelink = False + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# +html_show_sphinx = False + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# +html_show_copyright = True + +# If this is not None, a ‘Last updated on:’ timestamp is inserted at every +# page bottom, using the given strftime() format. +# +html_last_updated_fmt = '%Y-%m-%d' + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# +# html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' +# +# html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# 'ja' uses this config value. +# 'zh' user can custom change `jieba` dictionary path. +# +# html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +# +# html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +# +htmlhelp_basename = 'WebAssemblydoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('a4paper' or 'letterpaper'). + 'papersize': 'a4paper', + + # The font size ('10pt', '11pt' or '12pt'). + 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # Don't type-set cross references with emphasis. + 'preamble': '\\renewcommand\\sphinxcrossref[1]{#1}\n\\externaldocument[Core-]{WebAssembly}[https://webassembly.github.io//'+repo+'/core/_download/WebAssembly.pdf]\n', + + # Latex figure (float) alignment + 'figure_align': 'htbp', + + # Fancy chapters [Bjarne, Sonny, Lenny, Glenn, Conny, Rejne] + 'fncychap': '\\usepackage[Sonny]{fncychap}', + + # Allow chapters to start on even pages + 'extraclassoptions': 'openany', + + 'extrapackages': '\\usepackage{xr-hyper}', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ( master_doc, + name + '.tex', + title, + author + '\\\\ \\hfill\\large ' + editor, + 'howto' + ), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# +latex_logo = logo + +# For "manual" documents [part, chapter, or section]. +# +latex_toplevel_sectioning = 'section' + +# If true, show page references after internal links. +# +latex_show_pagerefs = False + +# How to show URL addresses after external links [no, footnote, inline]. +# +latex_show_urls = 'footnote' + +# Documents to append as an appendix to all manuals. +# +# latex_appendices = [] + +# It false, will not define \strong, \code, \titleref, \crossref ... but only +# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added +# packages. +# +# latex_keep_old_macro_names = True + +# If false, no module index is generated. +# +latex_domain_indices = False + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ( master_doc, + name, + title, + [author], + 1 + ) +] + +# If true, show URL addresses after external links. +# +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( master_doc, + name, + title, + author, + name, + 'A portable low-level execution format.', + 'Virtual Machine' + ), +] + +# Documents to append as an appendix to all manuals. +# +# texinfo_appendices = [] + +# If false, no module index is generated. +# +texinfo_domain_indices = False + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# +# texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = copyright + +# The basename for the epub file. It defaults to the project name. +# epub_basename = project + +# The HTML theme for the epub output. Since the default themes are not +# optimized for small screen space, using the same theme for HTML and epub +# output is usually not wise. This defaults to 'epub', a theme designed to save +# visual space. +# +# epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or 'en' if the language is not set. +# +# epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +# epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +# +# epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +# +# epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# +# epub_pre_files = [] + +# HTML files that should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# +# epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +# +# epub_tocdepth = 3 + +# Allow duplicate toc entries. +# +# epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +# +# epub_tocscope = 'default' + +# Fix unsupported image types using the Pillow. +# +# epub_fix_images = False + +# Scale large images. +# +# epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# +# epub_show_urls = 'inline' + +# If false, no index is generated. +# +# epub_use_index = True + +# Macros +rst_prolog = """ +.. |issuelink| replace:: https://github.com/webassembly/""" + repo + """/issues/ +.. |pagelink| replace:: https://webassembly.github.io/""" + repo + """/core/ +.. include:: /""" + core_dir + """/util/macros.def +.. include:: /""" + pwd + """/util/macros.def +""" + +# https://www.sphinx-doc.org/en/master/usage/extensions/math.html#confval-mathjax3_config +# https://docs.mathjax.org/en/latest/web/configuration.html#configuration +# https://docs.mathjax.org/en/latest/options/input/tex.html#tex-maxbuffer +mathjax3_config = { + 'tex': { 'maxBuffer': 30*1024 }, +} diff --git a/document/metadata/code/index.rst b/document/metadata/code/index.rst new file mode 100644 index 000000000..748524482 --- /dev/null +++ b/document/metadata/code/index.rst @@ -0,0 +1,15 @@ +WebAssembly Code Metadata +========================= + +.. only:: html + + | Editor: Yuri Iozzelli + + | Issue Tracker: |WasmIssues| + +.. toctree:: + :maxdepth: 1 + + intro + binary + text diff --git a/document/metadata/code/intro.rst b/document/metadata/code/intro.rst new file mode 100644 index 000000000..59503b413 --- /dev/null +++ b/document/metadata/code/intro.rst @@ -0,0 +1,18 @@ +.. _intro: + +Introduction +============ + +This document defines a generic mechanism for attaching arbitrary metadata to WebAssembly instructions. +Additionally, it defines specific metadata formats using this mechanism. + +Such metadata does not contribute to, or otherwise affect, the WebAssembly semantics, and may be ignored by an implementation. + +However, it can provides useful information that implementations can make use of to improve user experience or take compilation hints. + +Dependencies +~~~~~~~~~~~~ + +This document is based on the WebAssembly core specification (|WasmDraft|), and makes use of +terms and definitions described there. These uses always link to the corresponding definition +in the core specification. diff --git a/document/metadata/code/static/custom.css b/document/metadata/code/static/custom.css new file mode 100644 index 000000000..33bb863d4 --- /dev/null +++ b/document/metadata/code/static/custom.css @@ -0,0 +1,78 @@ +a { + color: #004BAB; + text-decoration: none; +} + +a.reference { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px dotted #004BAB; +} + +body { + font-size: 15px; +} + +div.document { width: 1000px; } +div.bodywrapper { margin: 0 0 0 200px; } +div.body { padding: 0 10px 0 10px; } +div.footer { width: 1000px; } + +div.body h1 { font-size: 200%; } +div.body h2 { font-size: 150%; } +div.body h3 { font-size: 120%; } +div.body h4 { font-size: 110%; } + +div.note { + border: 0px; + font-size: 90%; + background-color: #F6F8FF; +} + +div.admonition { + padding: 10px; +} + +div.admonition p.admonition-title { + margin: 0px 0px 0px 0px; + font-size: 100%; + font-weight: bold; +} + +div.math { + background-color: #F0F0F0; + padding: 3px 0 3px 0; + overflow-x: auto; + overflow-y: hidden; +} + +div.relations { + display: block; +} + +div.sphinxsidebar { + z-index: 1; + background: #FFF; + margin-top: -30px; + font-size: 13px; + width: 200px; + height: 100%; +} + +div.sphinxsidebarwrapper p.logo { + padding: 30px 40px 10px 0px; +} + +div.sphinxsidebar h3 { + font-size: 0px; +} + +div.sphinxsidebar a { + border-bottom: 0px; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px dotted; +} diff --git a/document/metadata/code/static/webassembly.png b/document/metadata/code/static/webassembly.png new file mode 100644 index 000000000..f9edc6109 Binary files /dev/null and b/document/metadata/code/static/webassembly.png differ diff --git a/document/metadata/code/text.rst b/document/metadata/code/text.rst new file mode 100644 index 000000000..c5ff1fbd0 --- /dev/null +++ b/document/metadata/code/text.rst @@ -0,0 +1,24 @@ +.. _text: + +Text Format +=========== + +.. _text-codemetadata: + +Code Metadata +------------- + +Code Metadata items appear in the text format as custom annotations, and are considered +attached to the first instruction that follows them. + + +.. math:: + \begin{array}{llclll} + \production{code metadata annotation} & \Tcodemetadataannot(\B{T}) &::=& + \text{(@metadata.code.T}~\X{data}{:}\B{T}~\text{)} \\ + \end{array} +.. index:: ! code metadata annotation + +Where `T` is the format name of the item, and `data` is a byte string containing the same +payload as in the binary format. + diff --git a/document/metadata/code/util/macros.def b/document/metadata/code/util/macros.def new file mode 100644 index 000000000..fbdd84140 --- /dev/null +++ b/document/metadata/code/util/macros.def @@ -0,0 +1,44 @@ +.. LINK MACROS + +.. External Standards +.. ------------------ + +.. External Definitions +.. -------------------- + + +.. MATH MACROS + +.. Abstract Syntax +.. --------------- + +.. Values, non-terminals + + +.. Binary Format +.. ------------- + +.. Code Metadata, non-terminals + +.. |Bcodemetadatasec| mathdef:: \xref{binary}{binary-codemetadata}{\B{codemetadatasec}} +.. |Bcodemetadata| mathdef:: \xref{binary}{binary-codemetadata}{\B{codemetadata}} +.. |Bcodemetadatafunc| mathdef:: \xref{binary}{binary-codemetadata}{\B{codemetadatafunc}} +.. |Bcodemetadataitem| mathdef:: \xref{binary}{binary-codemetadata}{\B{codemetadataitem}} + +.. Branch Hints, non-terminals + +.. |Bbranchhintsec| mathdef:: \xref{binary}{binary-branchhints}{\B{branchhintsec}} +.. |Bbranchhint| mathdef:: \xref{binary}{binary-branchhints}{\B{branchhint}} +.. |Bunlikely| mathdef:: \xref{binary}{binary-branchhints}{\B{unlikely}} +.. |Blikely| mathdef:: \xref{binary}{binary-branchhints}{\B{likely}} + + +.. Text Format +.. ----------- + + \production{code metadata annotation} & \Tcodemetadataannot(\B{T}) &::=& + +.. Branch Hints, non-terminals + +.. |Tcodemetadataannot| mathdef:: \xref{text}{text-codemetadata}{\T{codemetadataannot}} + diff --git a/document/metadata/code/util/mathdef.py b/document/metadata/code/util/mathdef.py new file mode 100644 index 000000000..57a6cf043 --- /dev/null +++ b/document/metadata/code/util/mathdef.py @@ -0,0 +1,128 @@ +from sphinx.directives.patches import MathDirective +from sphinx.util.texescape import tex_replace_map +from sphinx.writers.html5 import HTML5Translator +from sphinx.writers.latex import LaTeXTranslator +from docutils import nodes +from docutils.nodes import math +from docutils.parsers.rst.directives.misc import Replace +from six import text_type +import re + + +# Transform \xref in math nodes + +xref_re = re.compile('\\\\xref\{([^}]*)\}\{([^}]*)\}', re.M) + +def html_hyperlink(file, id): + if "/" in file: + return '\\href{../../core/%s.html#%s}' % (file, id.replace('_', '-')) + else: + return '\\href{%s.html#%s}' % (file, id.replace('_', '-')) + +def html_transform_math_xref(node): + new_text = xref_re.sub(lambda m: html_hyperlink(m.group(1), m.group(2)), node.astext()) + node.children[0] = nodes.Text(new_text) + +# Mirrors sphinx/writers/latex +def latex_hyperlink(file, id): + id = text_type(id).translate(tex_replace_map).\ + encode('ascii', 'backslashreplace').decode('ascii').\ + replace('_', '-').replace('\\', '_') + if "/" in file: + return '\\hyperref[Core-%s:%s]' % (file, id) + else: + return '\\hyperref[%s:%s]' % (file, id) + +def latex_transform_math_xref(node): + new_text = xref_re.sub(lambda m: latex_hyperlink(m.group(1), m.group(2)), node.astext()) + node.children[0] = nodes.Text(new_text) + +# Expand mathdef names in math roles and directives + +def_re = re.compile('\\\\[A-Za-z][0-9A-Za-z]*', re.M) + +auxcounter = 0 + +def lookup_mathdef(defs, name): + if name in defs: + [arity, s] = defs[name] + if arity > 0: + global auxcounter + auxcounter = auxcounter + 1 + name = "\\mathdef%d" % auxcounter + s = "\\def%s#%d{%s}%s" % (name, arity, s, name) + return s + return name + +def replace_mathdefs(doc, s): + if not hasattr(doc, 'mathdefs'): + return s + return def_re.sub(lambda m: lookup_mathdef(doc.mathdefs, m.group(0)), s) + +def ext_math_role(role, raw, text, line, inliner, options = {}, content = []): + text = replace_mathdefs(inliner.document, raw.split('`')[1]) + return [math(raw, text)], [] + +class ExtMathDirective(MathDirective): + def run(self): + doc = self.state.document + for i, s in enumerate(self.content): + self.content[i] = replace_mathdefs(doc, s) + for i, s in enumerate(self.arguments): + self.arguments[i] = replace_mathdefs(doc, s) + return super().run() + +class MathdefDirective(Replace): + def run(self): + name = '\\' + self.state.parent.rawsource.split('|')[1] + name = name.split('#') + if len(name) > 1: + arity = int(name[1]) + else: + arity = 0 + name = name[0] + doc = self.state.document + if not hasattr(doc, 'mathdefs'): + doc.mathdefs = {} + # TODO: we don't ever hit the case where len(self.content) > 1 + for i, s in enumerate(self.content): + self.content[i] = replace_mathdefs(doc, s) + doc.mathdefs[name] = [arity, ''.join(self.content)] + self.content[0] = ':math:`' + self.content[0] + self.content[-1] = self.content[-1] + '`' + return super().run() + +class WebAssemblyHTML5Translator(HTML5Translator): + """ + Customize HTML5Translator. + Convert xref in math and math block nodes to hrefs. + """ + def visit_math(self, node, math_env = ''): + html_transform_math_xref(node) + super().visit_math(node, math_env) + + def visit_math_block(self, node, math_env = ''): + html_transform_math_xref(node) + super().visit_math_block(node, math_env) + +class WebAssemblyLaTeXTranslator(LaTeXTranslator): + """ + Customize LaTeXTranslator. + Convert xref in math and math block nodes to hyperrefs. + """ + def visit_math(self, node): + latex_transform_math_xref(node) + super().visit_math(node) + + def visit_math_block(self, node): + latex_transform_math_xref(node) + super().visit_math_block(node) + +# Setup + +def setup(app): + app.set_translator('html', WebAssemblyHTML5Translator) + app.set_translator('latex', WebAssemblyLaTeXTranslator) + app.add_role('math', ext_math_role) + app.add_directive('math', ExtMathDirective, override = True) + app.add_directive('mathdef', MathdefDirective) diff --git a/document/util/check-echidna-status.py b/document/util/check-echidna-status.py new file mode 100755 index 000000000..9bc3f6026 --- /dev/null +++ b/document/util/check-echidna-status.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +from datetime import datetime, timedelta +import json +import os +import requests +import sys +import time + +ECHIDNA_ID_FILE = 'WD-echidna-id.txt' +ECHIDNA_STATUS_URL = 'http://labs.w3.org/echidna/api/status?id=' + + +def get_echidna_id(directory): + id_file = os.path.join(directory, ECHIDNA_ID_FILE) + file_time = os.path.getmtime(id_file) + if datetime.fromtimestamp(file_time) < datetime.now() - timedelta(hours=1): + print(f'Warning: Echidna ID is not recent: timestamp is {file_time}') + with open(id_file, 'r') as f: + return f.read().strip() + + +def get_current_response(echidna_id): + url = ECHIDNA_STATUS_URL + echidna_id + print(f'Fetching {url}') + response = requests.get(url, allow_redirects=True) + if response.status_code != 200: + print(f'Got status code {response.status_code}, text:') + print(response.text) + raise Exception('Failed to fetch echidna result') + + return response.json() + + +def get_echidna_result(echidna_id): + response = get_current_response(echidna_id) + while response['results']['status'] == 'started': + time.sleep(5) + print('Echidna run in progress, retrying...') + response = get_current_response(echidna_id) + + result = response['results']['status'] + print(f'Echidna issue {echidna_id} is {result}.') + print(json.dumps(response, indent=2)) + return result == 'success' + + +def main(argv): + directory = os.getcwd() if len(argv) < 2 else argv[1] + echidna_id = get_echidna_id(directory) + print(f'Got echidna id {echidna_id}.') + time.sleep(5) + if not get_echidna_result(echidna_id): + sys.exit(1) + + +if __name__ == '__main__': + main(sys.argv) diff --git a/document/util/htmldiff.pl b/document/util/htmldiff.pl index 4c2780c83..31ecafef8 100755 --- a/document/util/htmldiff.pl +++ b/document/util/htmldiff.pl @@ -48,7 +48,7 @@ # also requires the perl modules Getopt::Std # # NOTE: The markup created by htmldiff may not validate against the HTML 4.0 -# DTD. This is because the algorithm is realtively simple, and there are +# DTD. This is because the algorithm is relatively simple, and there are # places in the markup content model where the span element is not allowed. # Htmldiff is NOT aware of these places. # diff --git a/document/versions/Makefile b/document/versions/Makefile new file mode 100644 index 000000000..c65f94b9e --- /dev/null +++ b/document/versions/Makefile @@ -0,0 +1,23 @@ +DIRS = core +DIRNAME = versions +BUILDDIR = _build + +.PHONY: all +all: + mkdir -p $(BUILDDIR)/html/$(DIRNAME) + cp -R $(DIRS) $(BUILDDIR)/html/$(DIRNAME)/ + +.PHONY: WD-tar +WD-tar: + +.PHONY: WD-echidna +WD-echidna: + +.PHONY: WD-echidna-CI +WD-echidna-CI: + +.PHONY: diff +diff: + +.PHONY: clean +clean: diff --git a/document/versions/core/WebAssembly-1.0.pdf b/document/versions/core/WebAssembly-1.0.pdf new file mode 100644 index 000000000..456d91c95 Binary files /dev/null and b/document/versions/core/WebAssembly-1.0.pdf differ diff --git a/document/versions/core/WebAssembly-2.0.pdf b/document/versions/core/WebAssembly-2.0.pdf new file mode 100644 index 000000000..3a43c0020 Binary files /dev/null and b/document/versions/core/WebAssembly-2.0.pdf differ diff --git a/document/versions/core/WebAssembly-3.0-draft.pdf b/document/versions/core/WebAssembly-3.0-draft.pdf new file mode 100644 index 000000000..1909a6257 Binary files /dev/null and b/document/versions/core/WebAssembly-3.0-draft.pdf differ diff --git a/document/web-api/Makefile b/document/web-api/Makefile index 675593d86..0deb3d9d4 100644 --- a/document/web-api/Makefile +++ b/document/web-api/Makefile @@ -1,14 +1,17 @@ +W3C_STATUS = ED BUILDDIR = _build STATICDIR = _static DOWNLOADDIR = _download NAME = WebAssembly -DECISION_URL = https://github.com/WebAssembly/meetings/blob/main/main/2017/WG-12-06.md +DECISION_URL = https://github.com/WebAssembly/meetings/blob/main/main/2024/WG-06-12.md TAR = tar +DEADLINE = $(shell date -d "+30 days" +%Y-%m-%d 2>/dev/null || date -v +30d +%Y-%m-%d) +ECHIDNA_DRYRUN = true .PHONY: all all: mkdir -p $(BUILDDIR)/html - bikeshed spec index.bs $(BUILDDIR)/html/index.html + bikeshed spec --md-status=$(W3C_STATUS) --md-deadline=$(DEADLINE) index.bs $(BUILDDIR)/html/index.html @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html." .PHONY: publish @@ -33,7 +36,6 @@ diff: all # macOS tar has no “--transform” option (only GNU tar does), so on macOS, # do “brew install tar” & run “make” like this: “TAR=gtar make -e WD-tar” WD-tar: all - bikeshed spec --md-status=WD index.bs $(BUILDDIR)/html/index.html $(TAR) -C $(BUILDDIR)/html --transform="s/index.html/Overview.html/" -cf $(BUILDDIR)/WD.tar index.html @echo "Built $(BUILDDIR)/WD.tar." @@ -48,7 +50,11 @@ WD-echidna: WD-tar curl 'https://labs.w3.org/echidna/api/request' \ --user '$(W3C_USERNAME):$(W3C_PASSWORD)' \ -F "tar=@$(BUILDDIR)/WD.tar" \ - -F "decision=$(DECISION_URL)" | tee $(BUILDDIR)/WD-echidna-id.txt + -F "decision=$(DECISION_URL)" \ + -F "dry-run=$(ECHIDNA_DRYRUN)" | tee $(BUILDDIR)/WD-echidna-id.txt + python3 ../util/check-echidna-status.py $(BUILDDIR) + @echo + @echo "Uploaded $(W3C_STATUS). Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" .PHONY: WD-echidna-CI WD-echidna-CI: WD-tar @@ -60,6 +66,8 @@ WD-echidna-CI: WD-tar curl 'https://labs.w3.org/echidna/api/request' \ -F "tar=@$(BUILDDIR)/WD.tar" \ -F "token=$(W3C_ECHIDNA_TOKEN_WEBAPI)" \ - -F "decision=$(DECISION_URL)" | tee $(BUILDDIR)/WD-echidna-id.txt + -F "decision=$(DECISION_URL)" \ + -F "dry-run=$(ECHIDNA_DRYRUN)" | tee $(BUILDDIR)/WD-echidna-id.txt + python3 ../util/check-echidna-status.py $(BUILDDIR) @echo - @echo "Published working draft. Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" + @echo "Uploaded $(W3C_STATUS). Check its status at https://labs.w3.org/echidna/api/status?id=`cat $(BUILDDIR)/WD-echidna-id.txt`" diff --git a/document/web-api/index.bs b/document/web-api/index.bs index efc6a6663..a63ee60bb 100644 --- a/document/web-api/index.bs +++ b/document/web-api/index.bs @@ -7,6 +7,7 @@ Issue Tracking: GitHub https://github.com/WebAssembly/spec/issues Level: 2 TR: https://www.w3.org/TR/wasm-web-api-2/ ED: https://webassembly.github.io/spec/web-api/ +Implementation Report: https://webassembly.org/features/ Editor: Ms2ger, w3cid 46309, Igalia Repository: WebAssembly/spec Abstract: This document describes the integration of WebAssembly with the broader web platform. @@ -17,12 +18,6 @@ Date: now
 {
-  "ECMA-262": {
-    "href": "https://tc39.github.io/ecma262",
-    "title": "ECMAScript® Language Specification",
-    "publisher": "ECMA TC39",
-    "status": "Current Editor's Draft"
-  },
   "WEBASSEMBLY": {
     "href": "https://webassembly.github.io/spec/core/",
     "title": "WebAssembly Core Specification",
@@ -45,7 +40,7 @@ Date: now
 
-urlPrefix: https://tc39.github.io/ecma262/; spec: ECMA-262
+urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
     type: exception; for: ECMAScript
         text: TypeError; url: sec-native-error-types-used-in-this-standard-typeerror
     type: interface
@@ -77,12 +72,16 @@ url:https://w3c.github.io/webappsec-secure-contexts/#environment-settings-object
 
+ +

Introduction

This document builds off of the WebAssembly specification [[WEBASSEMBLY]] and the WebAssembly JavaScript embedding [[WASMJS]]. +It describes the integration of WebAssembly into the broader Web platform, for example with +additional APIs that are implemented by Web user agents but are outside the scope of JavaScript [[ECMASCRIPT]] itself.

Streaming Module Compilation and Instantiation

@@ -241,8 +240,8 @@ JavaScript.

Media-type Registration

-This section will be submitted to the Internet Engineering Steering Group (IESG) for -review, approval, and registration with IANA. +The media type `application/wasm` has been registered with the IANA media type database [[IANA-MEDIA-TYPES]], +with the following registration template: application/wasm @@ -311,3 +310,27 @@ application/wasm
Author/Change Controller:
W3C
+ + +

Security and Privacy Considerations

+ +

This section is non-normative.

+WebAssembly provides no access to the surrounding environment other than via the JavaScript API described in the [[WASMJS|JS API]] specification. +Therefore, WebAssembly cannot collect or expose any information (personal, sensitive or otherwise) to Web sites or other parties beyond what can be collected, exposed or processed with JavaScript. +WebAssembly memory has the same lifetime as the objects in the surrounding JavaScript environment and is not persisted or serialized (other than by copying it out to JavaScript and using existing serialization APIs). +No access is provided to the underlying platform or hardware, or to other devices, or to the user agent’s native UI. + +WebAssembly is an additional program execution mechanism, and can be executed wherever JavaScript can be executed. +Therefore the threat model is essentially the same as for JavaScript code, and has similar considerations for delivery (e.g. WebAssembly code should be protected in transit from active and passive network attackers) +and policy (e.g. some loading mechanisms or execution are restricted via mechanisms such as the same-origin policy or Content Security Policy). + +

Change History

+ +

This section is non-normative.

+ +

Since the original release 1.0 of the WebAssembly specification, a number of proposals for extensions have been integrated. +The following sections provide an overview of what has changed.

+ +

Release 2.0

+

Media-type Registraton Completed

+The registration for the `application/wasm` media type has been successfully completed. diff --git a/interpreter/Makefile b/interpreter/Makefile index dfd9064bb..30c798936 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -24,7 +24,8 @@ JS = # set to JS shell command to run JS tests, empty to skip .PHONY: default all ci jslib zip default: $(NAME) -all: default partest +all: default alltest +alltest: unittest partest custompartest ci: all jslib zip jslib: $(JSLIB) @@ -59,9 +60,10 @@ unittest: $(UNITTESTS:%=unittest/%) unittest/%: dune build $(@F).exe dune exec ./$(@F).exe + @echo All unit tests passed. -# Test suite +# Core test suite TESTDIR = ../test/core TESTFILES = $(shell cd $(TESTDIR) > /dev/null; ls *.wast; ls [a-z]*/*.wast) @@ -69,7 +71,7 @@ TESTS = $(TESTFILES:%.wast=%) .PHONY: test partest quiettest -test: $(NAME) unittest +test: $(NAME) $(TESTDIR)/run.py --wasm `pwd`/$(NAME) $(if $(JS),--js '$(JS)',) test/%: $(NAME) @@ -78,7 +80,7 @@ test/%: $(NAME) run/%: $(NAME) ./$(NAME) $(TESTDIR)/$*.wast -partest: $(NAME) unittest +partest: $(NAME) make -j10 quiettest quiettest: $(TESTS:%=quiettest/%) @@ -92,6 +94,36 @@ quiettest/%: $(NAME) (cat $(@F).out && rm $(@F).out && exit 1) +# Custom test suite + +CUSTOMTESTDIR = ../test/custom +CUSTOMTESTDIRS = $(shell cd $(CUSTOMTESTDIR); ls -d [a-z]*) +CUSTOMTESTFILES = $(shell cd $(CUSTOMTESTDIR); ls [a-z]*/*.wast) +CUSTOMTESTS = $(CUSTOMTESTFILES:%.wast=%) +CUSTOMOPTS = -c custom $(CUSTOMTESTDIRS:%=-c %) + +.PHONY: customtest custompartest customquiettest + +customtest: $(NAME) + $(TESTDIR)/run.py --wasm `pwd`/$(NAME) --opts '$(CUSTOMOPTS)' $(if $(JS),--js '$(JS)',) $(CUSTOMTESTFILES:%=$(CUSTOMTESTDIR)/%) + +customtest/%: $(NAME) + $(TESTDIR)/run.py --wasm `pwd`/$(NAME) --opts '$(CUSTOMOPTS) ' $(if $(JS),--js '$(JS)',) $(CUSTOMTESTDIR)/$*.wast + +customrun/%: $(NAME) + ./$(NAME) $(CUSTOMOPTS) $(CUSTOMTESTDIR)/$*.wast + +custompartest: $(CUSTOMTESTS:%=customquiettest/%) + @echo All custom tests passed. + +customquiettest/%: $(NAME) + @ ( \ + $(TESTDIR)/run.py 2>$(@F).out --wasm `pwd`/$(NAME) --opts '$(CUSTOMOPTS)' $(if $(JS),--js '$(JS)',) $(CUSTOMTESTDIR)/$*.wast && \ + rm $(@F).out \ + ) || \ + cat $(@F).out || rm $(@F).out || exit 1 + + # Packaging .PHONY: install diff --git a/interpreter/README.md b/interpreter/README.md index 8aa7e9ae1..f95f4b6bb 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -426,8 +426,9 @@ In order to be able to check and run modules for testing purposes, the S-express script: * cmd: - ;; define, validate, and initialize module - ( register ? ) ;; register module for imports + ;; define, validate, and possibly instantiate module + + ( register ? ) ;; register module instance for imports ;; perform action and print results ;; assert result of an action ;; meta command @@ -436,6 +437,10 @@ module: ... ( module ? binary * ) ;; module in binary format (may be malformed) ( module ? quote * ) ;; module quoted in text (may be malformed) + ( module definition ? binary ... ) ;; uninstantiated module + +instance: + ( module instance ? ? ) ;; instantiate latter module to former instance action: ( invoke ? * ) ;; invoke function export @@ -455,8 +460,8 @@ assertion: ( assert_exhaustion ) ;; assert action exhausts system resources ( assert_malformed ) ;; assert module cannot be decoded with given failure string ( assert_invalid ) ;; assert module is invalid with given failure string - ( assert_unlinkable ) ;; assert module fails to link - ( assert_trap ) ;; assert module traps on instantiation + ( assert_unlinkable ) ;; assert module fails to link + ( assert_trap ) ;; assert module traps on instantiation result_pat: ( .const ) @@ -466,6 +471,7 @@ result_pat: ( ref.func ) ( ref.extern ) ( ref. ) + ( either + ) ;; alternative results num_pat: ;; literal result @@ -485,6 +491,11 @@ The script format supports additional syntax for defining modules. A module of the form `(module binary *)` is given in binary form and will be decoded from the (concatenation of the) strings. A module of the form `(module quote *)` is given in textual form and will be parsed from the (concatenation of the) strings. In both cases, decoding/parsing happens when the command is executed, not when the script is parsed, so that meta commands like `assert_malformed` can be used to check expected errors. +Usually, a module declaration implicitly instantiates the module, +that is, it defines both a module and an instance (of the same name). +Instantiation can be suppressed by adding the keyword `definition`. +A module declared as a definition only can then be instantiated explicitly, and multiple times, using the separate form `(module instance )`. + There are also a number of meta commands. The `script` command is a simple mechanism to name sub-scripts themselves. This is mainly useful for converting scripts with the `output` command. Commands inside a `script` will be executed normally, but nested meta are expanded in place (`input`, recursively) or elided (`output`) in the named script. @@ -492,6 +503,7 @@ The `input` and `output` meta commands determine the requested file format from The interpreter supports a "dry" mode (flag `-d`), in which modules are only validated. In this mode, all actions and assertions are ignored. It also supports an "unchecked" mode (flag `-u`), in which module definitions are not validated before use. +In that mode, execution may fail with a "crash" error message. ### Spectest host module @@ -537,6 +549,10 @@ cmd: module: ( module ? binary * ) ;; module in binary format (may be malformed) + ( module definition ? binary ... ) ;; uninstantiated module + +instance: + ( module instance ? ? ) ;; instantiate latter module to former instance action: ( invoke ? * ) ;; invoke function export @@ -549,8 +565,8 @@ assertion: ( assert_exhaustion ) ;; assert action exhausts system resources ( assert_malformed ) ;; assert module cannot be decoded with given failure string ( assert_invalid ) ;; assert module is invalid with given failure string - ( assert_unlinkable ) ;; assert module fails to link - ( assert_trap ) ;; assert module traps on instantiation + ( assert_unlinkable ) ;; assert module fails to link + ( assert_trap ) ;; assert module traps on instantiation result_pat: ( .const ) @@ -559,6 +575,7 @@ result_pat: ( ref.func ) ( ref.extern ) ( ref. ) + ( either + ) ;; alternative results num_pat: ;; literal result diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index ae08c3f2b..749007f32 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -100,8 +100,8 @@ let rec sN n s = then (if b land 0x40 = 0 then x else Int64.(logor x (logxor (-1L) 0x7fL))) else Int64.(logor x (shift_left (sN (n - 7) s) 7)) -let u1 s = Int64.to_int (uN 1 s) let u32 s = Int64.to_int32 (uN 32 s) +let u64 s = uN 64 s let s7 s = Int64.to_int (sN 7 s) let s32 s = Int64.to_int32 (sN 32 s) let s33 s = I32_convert.wrap_i64 (sN 33 s) @@ -116,7 +116,6 @@ let len32 s = if I32.le_u n (Int32.of_int (len s - pos)) then Int32.to_int n else error s pos "length out of bounds" -let bool s = (u1 s = 1) let string s = let n = len32 s in get_string n s let rec list f n s = if n = 0 then [] else let x = f s in x :: list f (n - 1) s let opt f b s = if b then Some (f s) else None @@ -180,6 +179,7 @@ let heap_type s = (fun s -> VarHT (var_type s33 s)); (fun s -> match s7 s with + | -0x0b -> NoContHT | -0x0c -> NoExnHT | -0x0d -> NoFuncHT | -0x0e -> NoExternHT @@ -192,6 +192,7 @@ let heap_type s = | -0x15 -> StructHT | -0x16 -> ArrayHT | -0x17 -> ExnHT + | -0x18 -> ContHT | _ -> error s pos "malformed heap type" ) ] s @@ -199,6 +200,7 @@ let heap_type s = let ref_type s = let pos = pos s in match s7 s with + | -0x0b -> (Null, NoContHT) | -0x0c -> (Null, NoExnHT) | -0x0d -> (Null, NoFuncHT) | -0x0e -> (Null, NoExternHT) @@ -211,6 +213,7 @@ let ref_type s = | -0x15 -> (Null, StructHT) | -0x16 -> (Null, ArrayHT) | -0x17 -> (Null, ExnHT) + | -0x18 -> (Null, ContHT) | -0x1c -> (NoNull, heap_type s) | -0x1d -> (Null, heap_type s) | _ -> error s pos "malformed reference type" @@ -253,11 +256,15 @@ let func_type s = let ts2 = result_type s in FuncT (ts1, ts2) +let cont_type s = + ContT (heap_type s) + let str_type s = match s7 s with | -0x20 -> DefFuncT (func_type s) | -0x21 -> DefStructT (struct_type s) | -0x22 -> DefArrayT (array_type s) + | -0x23 -> DefContT (cont_type s) (* TODO(dhil): See comment in encode.ml *) | _ -> error s (pos s - 1) "malformed definition type" let sub_type s = @@ -279,28 +286,32 @@ let rec_type s = let limits uN s = - let has_max = bool s in + let flags = byte s in + require (flags land 0xfa = 0) s (pos s - 1) "malformed limits flags"; + let has_max = (flags land 1 = 1) in + let at = if flags land 4 = 4 then I64AT else I32AT in let min = uN s in let max = opt uN has_max s in - {min; max} + at, {min; max} let table_type s = let t = ref_type s in - let lim = limits u32 s in - TableT (lim, t) + let at, lim = limits u64 s in + TableT (at, lim, t) let memory_type s = - let lim = limits u32 s in - MemoryT lim + let at, lim = limits u64 s in + MemoryT (at, lim) + +let tag_type s = + zero s; + at var s let global_type s = let t = val_type s in let mut = mutability s in GlobalT (mut, t) -let tag_type s = - zero s; at var s - (* Instructions *) @@ -317,7 +328,7 @@ let memop s = let has_var = Int32.logand flags 0x40l <> 0l in let x = if has_var then at var s else Source.(0l @@ no_region) in let align = Int32.(to_int (logand flags 0x3fl)) in - let offset = u32 s in + let offset = u64 s in x, align, offset let block_type s = @@ -340,6 +351,16 @@ let locals s = s pos "too many locals"; List.flatten (List.map (Lib.Fun.uncurry Lib.List32.make) nts) +let on_clause s = + match byte s with + | 0x00 -> + let x = at var s in + let y = at var s in + (x, OnLabel y) + | 0x01 -> + let x = at var s in + (x, OnSwitch) + | _ -> error s (pos s) "ON opcode expected" let rec instr s = let pos = pos s in @@ -399,7 +420,10 @@ let rec instr s = | 0x14 -> call_ref (at var s) | 0x15 -> return_call_ref (at var s) - | 0x16 | 0x17 | 0x18 | 0x19 as b -> illegal s pos b + | (0x16 | 0x17) as b -> illegal s pos b + + | 0x18 -> error s pos "misplaced DELEGATE opcode" + | 0x19 -> error s pos "misplaced CATCH_ALL opcode" | 0x1a -> drop | 0x1b -> select None @@ -607,6 +631,30 @@ let rec instr s = | 0xd5 -> br_on_null (at var s) | 0xd6 -> br_on_non_null (at var s) + | 0xe0 -> cont_new (at var s) + | 0xe1 -> + let x = at var s in + let y = at var s in + cont_bind x y + | 0xe2 -> suspend (at var s) + | 0xe3 -> + let x = at var s in + let xls = vec on_clause s in + resume x xls + | 0xe4 -> + let x = at var s in + let tag = at var s in + let xls = vec on_clause s in + resume_throw x tag xls + | 0xe5 -> + let x = at var s in + let xls = vec on_clause s in + resume_throw_ref x xls + | 0xe6 -> + let x = at var s in + let y = at var s in + switch x y + | 0xfb as b -> (match u32 s with | 0x00l -> struct_new (at var s) @@ -963,6 +1011,26 @@ let rec instr s = | 0xfdl -> i32x4_trunc_sat_f64x2_u_zero | 0xfel -> f64x2_convert_low_i32x4_s | 0xffl -> f64x2_convert_low_i32x4_u + | 0x100l -> i8x16_relaxed_swizzle + | 0x101l -> i32x4_relaxed_trunc_f32x4_s + | 0x102l -> i32x4_relaxed_trunc_f32x4_u + | 0x103l -> i32x4_relaxed_trunc_f64x2_s_zero + | 0x104l -> i32x4_relaxed_trunc_f64x2_u_zero + | 0x105l -> f32x4_relaxed_madd + | 0x106l -> f32x4_relaxed_nmadd + | 0x107l -> f64x2_relaxed_madd + | 0x108l -> f64x2_relaxed_nmadd + | 0x109l -> i8x16_relaxed_laneselect + | 0x10al -> i16x8_relaxed_laneselect + | 0x10bl -> i32x4_relaxed_laneselect + | 0x10cl -> i64x2_relaxed_laneselect + | 0x10dl -> f32x4_relaxed_min + | 0x10el -> f32x4_relaxed_max + | 0x10fl -> f64x2_relaxed_min + | 0x110l -> f64x2_relaxed_max + | 0x111l -> i16x8_relaxed_q15mulr_s + | 0x112l -> i16x8_relaxed_dot_i8x16_i7x16_s + | 0x113l -> i32x4_relaxed_dot_i8x16_i7x16_add_s | n -> illegal s pos (I32.to_int_u n) ) @@ -971,11 +1039,11 @@ let rec instr s = and instr_block s = List.rev (instr_block' s []) and instr_block' s es = match peek s with - | None | Some (0x05 | 0x0b) -> es + | None | Some (0x05 | 0x07 | 0x0b | 0x19) -> es | _ -> let pos = pos s in let e' = instr s in - instr_block' s ((e' @@ region s pos pos) :: es) + instr_block' s (Source.(e' @@ region s pos pos) :: es) and catch s = match byte s with @@ -1003,20 +1071,20 @@ let id s = let bo = peek s in Lib.Option.map (function - | 0 -> `CustomSection - | 1 -> `TypeSection - | 2 -> `ImportSection - | 3 -> `FuncSection - | 4 -> `TableSection - | 5 -> `MemorySection - | 6 -> `GlobalSection - | 7 -> `ExportSection - | 8 -> `StartSection - | 9 -> `ElemSection - | 10 -> `CodeSection - | 11 -> `DataSection - | 12 -> `DataCountSection - | 13 -> `TagSection + | 0 -> Custom.Custom + | 1 -> Custom.Type + | 2 -> Custom.Import + | 3 -> Custom.Func + | 4 -> Custom.Table + | 5 -> Custom.Memory + | 6 -> Custom.Global + | 7 -> Custom.Export + | 8 -> Custom.Start + | 9 -> Custom.Elem + | 10 -> Custom.Code + | 11 -> Custom.Data + | 12 -> Custom.DataCount + | 13 -> Custom.Tag | _ -> error s (pos s) "malformed section id" ) bo @@ -1034,7 +1102,7 @@ let section tag f default s = let type_ s = at rec_type s let type_section s = - section `TypeSection (vec type_) [] s + section Custom.Type (vec type_) [] s (* Import section *) @@ -1055,13 +1123,13 @@ let import s = {module_name; item_name; idesc} let import_section s = - section `ImportSection (vec (at import)) [] s + section Custom.Import (vec (at import)) [] s (* Function section *) let func_section s = - section `FuncSection (vec (at var)) [] s + section Custom.Func (vec (at var)) [] s (* Table section *) @@ -1077,13 +1145,13 @@ let table s = ); (fun s -> let at = region s (pos s) (pos s) in - let TableT (_, (_, ht)) as ttype = table_type s in + let TableT (_, _at, (_, ht)) as ttype = table_type s in {ttype; tinit = [RefNull ht @@ at] @@ at} ); ] s let table_section s = - section `TableSection (vec (at table)) [] s + section Custom.Table (vec (at table)) [] s (* Memory section *) @@ -1093,7 +1161,8 @@ let memory s = {mtype} let memory_section s = - section `MemorySection (vec (at memory)) [] s + section Custom.Memory (vec (at memory)) [] s + (* Tag section *) @@ -1102,7 +1171,9 @@ let tag s = {tgtype} let tag_section s = - section `TagSection (vec (at tag)) [] s + section Custom.Tag (vec (at tag)) [] s + + (* Global section *) @@ -1112,7 +1183,7 @@ let global s = {gtype; ginit} let global_section s = - section `GlobalSection (vec (at global)) [] s + section Custom.Global (vec (at global)) [] s (* Export section *) @@ -1132,7 +1203,7 @@ let export s = {name; edesc} let export_section s = - section `ExportSection (vec (at export)) [] s + section Custom.Export (vec (at export)) [] s (* Start section *) @@ -1142,7 +1213,7 @@ let start s = {sfunc} let start_section s = - section `StartSection (opt (at start) true) None s + section Custom.Start (opt (at start) true) None s (* Code section *) @@ -1154,7 +1225,7 @@ let code _ s = {locals; body; ftype = -1l @@ no_region} let code_section s = - section `CodeSection (vec (at (sized code))) [] s + section Custom.Code (vec (at (sized code))) [] s (* Element section *) @@ -1208,7 +1279,7 @@ let elem s = | 0x04l -> let emode = at active_zero s in let einit = vec const s in - {etype = (NoNull, FuncHT); einit; emode} + {etype = (Null, FuncHT); einit; emode} | 0x05l -> let emode = at passive s in let etype = ref_type s in @@ -1227,7 +1298,7 @@ let elem s = | _ -> error s (pos s - 1) "malformed elements segment kind" let elem_section s = - section `ElemSection (vec (at elem)) [] s + section Custom.Elem (vec (at elem)) [] s (* Data section *) @@ -1249,7 +1320,7 @@ let data s = | _ -> error s (pos s - 1) "malformed data segment kind" let data_section s = - section `DataSection (vec (at data)) [] s + section Custom.Data (vec (at data)) [] s (* DataCount section *) @@ -1258,64 +1329,66 @@ let data_count s = Some (u32 s) let data_count_section s = - section `DataCountSection data_count None s + section Custom.DataCount data_count None s (* Custom section *) -let custom size s = +let custom place size s = let start = pos s in - let id = name s in - let bs = get_string (size - (pos s - start)) s in - Some (id, bs) + let name = name s in + let content = get_string (size - (pos s - start)) s in + Custom.{name; content; place} -let custom_section s = - section_with_size `CustomSection custom None s +let some_custom place size s = + Some (at (custom place size) s) -let non_custom_section s = - match id s with - | None | Some `CustomSection -> None - | _ -> skip 1 s; sized skip s; Some () +let custom_section place s = + section_with_size Custom.Custom (some_custom place) None s (* Modules *) -let rec iterate f s = if f s <> None then iterate f s +let rec iterate f s = + match f s with + | None -> [] + | Some x -> x :: iterate f s let magic = 0x6d736100l let module_ s = + let open Custom in let header = word32 s in require (header = magic) s 0 "magic header not detected"; let version = word32 s in require (version = Encode.version) s 4 "unknown binary version"; - iterate custom_section s; + let customs = iterate (custom_section (Before Type)) s in let types = type_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Type)) s in let imports = import_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Import)) s in let func_types = func_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Func)) s in let tables = table_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Table)) s in let memories = memory_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Memory)) s in let tags = tag_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Tag)) s in let globals = global_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Global)) s in let exports = export_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Export)) s in let start = start_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Start)) s in let elems = elem_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Elem)) s in let data_count = data_count_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After DataCount)) s in let func_bodies = code_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Code)) s in let datas = data_section s in - iterate custom_section s; + let customs = customs @ iterate (custom_section (After Data)) s in require (pos s = len s) s (len s) "unexpected content after last section"; require (List.length func_types = List.length func_bodies) s (len s) "function and code section have inconsistent lengths"; @@ -1325,24 +1398,37 @@ let module_ s = List.for_all Free.(fun f -> (func f).datas = Set.empty) func_bodies) s (len s) "data count section required"; let funcs = - List.map2 (fun t f -> {f.it with ftype = t} @@ f.at) func_types func_bodies - in { types; tables; memories; globals; tags; funcs; - imports; exports; elems; datas; start } - - -let decode name bs = at module_ (stream name bs) - -let all_custom tag s = - let header = word32 s in - require (header = magic) s 0 "magic header not detected"; - let version = word32 s in - require (version = Encode.version) s 4 "unknown binary version"; - let rec collect () = - iterate non_custom_section s; - match custom_section s with - | None -> [] - | Some (n, s) when n = tag -> s :: collect () - | Some _ -> collect () - in collect () - -let decode_custom tag name bs = all_custom tag (stream name bs) + List.map2 Source.(fun t f -> {f.it with ftype = t} @@ f.at) + func_types func_bodies + in + { types; tables; memories; tags; globals; funcs; + imports; exports; elems; datas; start }, + customs + +let decode_custom m bs custom = + let open Source in + let Custom.{name; content; place} = custom.it in + match Custom.handler name, Custom.handler (Utf8.decode "custom") with + | Some (module Handler), _ -> + let fmt = Handler.decode m bs custom in + let module S = struct module Handler = Handler let it = fmt end in + [(module S : Custom.Section)] + | None, Some (module Handler') -> + let fmt = Handler'.decode m bs custom in + let module S = struct module Handler = Handler' let it = fmt end in + [(module S : Custom.Section)] + | None, None -> + if !Flags.custom_reject then + raise (Custom.Code (custom.at, + "unknown custom section \"" ^ Utf8.encode name ^ "\"")) + else + [] + +let decode_with_custom name bs = + let m_cs = at module_ (stream name bs) in + let open Source in + let m', cs = m_cs.it in + let m = m' @@ m_cs.at in + m, List.flatten (List.map (decode_custom m bs) cs) + +let decode name bs = fst (decode_with_custom name bs) diff --git a/interpreter/binary/decode.mli b/interpreter/binary/decode.mli index 4460023d2..b02b33236 100644 --- a/interpreter/binary/decode.mli +++ b/interpreter/binary/decode.mli @@ -1,5 +1,4 @@ exception Code of Source.region * string val decode : string -> string -> Ast.module_ (* raises Code *) - -val decode_custom : Ast.name -> string -> string -> string list (* raises Code *) +val decode_with_custom : string -> string -> Ast.module_ * Custom.section list (* raises Code *) diff --git a/interpreter/binary/encode.ml b/interpreter/binary/encode.ml index a370c113a..ca32b89bf 100644 --- a/interpreter/binary/encode.ml +++ b/interpreter/binary/encode.ml @@ -61,7 +61,6 @@ struct if -64L <= i && i < 64L then byte b else (byte (b lor 0x80); s64 (Int64.shift_right i 7)) - let u1 i = u64 Int64.(logand (of_int i) 1L) let u32 i = u64 Int64.(logand (of_int32 i) 0xffffffffL) let s7 i = s64 (Int64.of_int i) let s32 i = s64 (Int64.of_int32 i) @@ -70,12 +69,13 @@ struct let f64 x = word64 (F64.to_bits x) let v128 v = String.iter (put s) (V128.to_bits v) + let flag b i = if b then 1 lsl i else 0 + let len i = if Int32.to_int (Int32.of_int i) <> i then Code.error Source.no_region "length out of bounds"; u32 (Int32.of_int i) - let bool b = u1 (if b then 1 else 0) let string bs = len (String.length bs); put_string s bs let name n = string (Utf8.encode n) let list f xs = List.iter f xs @@ -130,6 +130,8 @@ struct | NoExnHT -> s7 (-0x0c) | ExternHT -> s7 (-0x11) | NoExternHT -> s7 (-0x0e) + | ContHT -> s7 (-0x18) + | NoContHT -> s7 (-0x0b) | VarHT x -> var_type s33 x | DefHT _ | BotHT -> assert false @@ -150,6 +152,8 @@ struct | (Null, NoExnHT) -> s7 (-0x0c) | (Null, ExternHT) -> s7 (-0x11) | (Null, NoExternHT) -> s7 (-0x0e) + | (Null, ContHT) -> s7 (-0x18) + | (Null, NoContHT) -> s7 (-0x0b) | (Null, t) -> s7 (-0x1d); heap_type t | (NoNull, t) -> s7 (-0x1c); heap_type t @@ -180,10 +184,17 @@ struct let func_type = function | FuncT (ts1, ts2) -> vec val_type ts1; vec val_type ts2 + let cont_type = function + | ContT ht -> heap_type ht + let str_type = function | DefStructT st -> s7 (-0x21); struct_type st | DefArrayT at -> s7 (-0x22); array_type at | DefFuncT ft -> s7 (-0x20); func_type ft + | DefContT ct -> s7 (-0x23); cont_type ct + (* TODO(dhil): This might need to change again in the future as a + different proposal might claim this opcode! GC proposal claimed + the previous opcode we were using. *) let sub_type = function | SubT (Final, [], st) -> str_type st @@ -194,21 +205,20 @@ struct | RecT [st] -> sub_type st | RecT sts -> s7 (-0x32); vec sub_type sts - let limits uN {min; max} = - bool (max <> None); uN min; opt uN max + let limits at {min; max} = + let flags = flag (max <> None) 0 + flag (at = I64AT) 2 in + byte flags; u64 min; opt u64 max let table_type = function - | TableT (lim, t) -> ref_type t; limits u32 lim + | TableT (at, lim, t) -> ref_type t; limits at lim let memory_type = function - | MemoryT lim -> limits u32 lim + | MemoryT (at, lim) -> limits at lim let global_type = function | GlobalT (mut, t) -> val_type t; mutability mut - let tag_type x = - u32 0x00l; var x - + let tag_type x = u32 0x00l; var x (* Expressions *) @@ -229,7 +239,7 @@ struct Int32.(logor (of_int align) (if has_var then 0x40l else 0x00l)) in u32 flags; if has_var then var x; - u32 offset + u64 offset let block_type = function | VarBlockType x -> var_type s33 (StatX x.it) @@ -245,6 +255,16 @@ struct | nlocs -> (1, loc) :: nlocs in vec local (List.fold_right combine locs []) + let on_clause (x, y) = + match y with + | OnSwitch -> + byte 0x01; var x + | OnLabel y -> + byte 0x00; var x; var y + + let resumetable xls = + vec on_clause xls + let rec instr e = match e.it with | Unreachable -> op 0x00 @@ -278,6 +298,14 @@ struct | ReturnCallRef x -> op 0x15; var x | ReturnCallIndirect (x, y) -> op 0x13; var y; var x + | ContNew x -> op 0xe0; var x + | ContBind (x, y) -> op 0xe1; var x; var y + | Suspend x -> op 0xe2; var x + | Resume (x, xls) -> op 0xe3; var x; resumetable xls + | ResumeThrow (x, y, xls) -> op 0xe4; var x; var y; resumetable xls + | ResumeThrowRef (x, xls) -> op 0xe5; var x; resumetable xls + | Switch (x, y) -> op 0xe6; var x; var y + | Throw x -> op 0x08; var x | ThrowRef -> op 0x0a @@ -741,6 +769,7 @@ struct | VecBinary (V128 (I8x16 V128Op.MaxS)) -> vecop 0x78l | VecBinary (V128 (I8x16 V128Op.MaxU)) -> vecop 0x79l | VecBinary (V128 (I8x16 V128Op.AvgrU)) -> vecop 0x7bl + | VecBinary (V128 (I8x16 V128Op.RelaxedSwizzle)) -> vecop 0x100l | VecBinary (V128 (I16x8 V128Op.NarrowS)) -> vecop 0x85l | VecBinary (V128 (I16x8 V128Op.NarrowU)) -> vecop 0x86l | VecBinary (V128 (I16x8 V128Op.Add)) -> vecop 0x8el @@ -760,6 +789,8 @@ struct | VecBinary (V128 (I16x8 V128Op.ExtMulLowU)) -> vecop 0x9el | VecBinary (V128 (I16x8 V128Op.ExtMulHighU)) -> vecop 0x9fl | VecBinary (V128 (I16x8 V128Op.Q15MulRSatS)) -> vecop 0x82l + | VecBinary (V128 (I16x8 V128Op.RelaxedQ15MulRS)) -> vecop 0x111l + | VecBinary (V128 (I16x8 V128Op.RelaxedDot)) -> vecop 0x112l | VecBinary (V128 (I32x4 V128Op.Add)) -> vecop 0xael | VecBinary (V128 (I32x4 V128Op.Sub)) -> vecop 0xb1l | VecBinary (V128 (I32x4 V128Op.MinS)) -> vecop 0xb6l @@ -787,6 +818,8 @@ struct | VecBinary (V128 (F32x4 V128Op.Max)) -> vecop 0xe9l | VecBinary (V128 (F32x4 V128Op.Pmin)) -> vecop 0xeal | VecBinary (V128 (F32x4 V128Op.Pmax)) -> vecop 0xebl + | VecBinary (V128 (F32x4 V128Op.RelaxedMin)) -> vecop 0x10dl + | VecBinary (V128 (F32x4 V128Op.RelaxedMax)) -> vecop 0x10el | VecBinary (V128 (F64x2 V128Op.Add)) -> vecop 0xf0l | VecBinary (V128 (F64x2 V128Op.Sub)) -> vecop 0xf1l | VecBinary (V128 (F64x2 V128Op.Mul)) -> vecop 0xf2l @@ -795,9 +828,23 @@ struct | VecBinary (V128 (F64x2 V128Op.Max)) -> vecop 0xf5l | VecBinary (V128 (F64x2 V128Op.Pmin)) -> vecop 0xf6l | VecBinary (V128 (F64x2 V128Op.Pmax)) -> vecop 0xf7l + | VecBinary (V128 (F64x2 V128Op.RelaxedMin)) -> vecop 0x10fl + | VecBinary (V128 (F64x2 V128Op.RelaxedMax)) -> vecop 0x110l | VecBinary (V128 _) -> error e.at "illegal binary vector instruction" + | VecTernary (V128 (F32x4 V128Op.RelaxedMadd)) -> vecop 0x105l + | VecTernary (V128 (F32x4 V128Op.RelaxedNmadd)) -> vecop 0x106l + | VecTernary (V128 (F64x2 V128Op.RelaxedMadd)) -> vecop 0x107l + | VecTernary (V128 (F64x2 V128Op.RelaxedNmadd)) -> vecop 0x108l + | VecTernary (V128 (I8x16 V128Op.RelaxedLaneselect)) -> vecop 0x109l + | VecTernary (V128 (I16x8 V128Op.RelaxedLaneselect)) -> vecop 0x10al + | VecTernary (V128 (I32x4 V128Op.RelaxedLaneselect)) -> vecop 0x10bl + | VecTernary (V128 (I64x2 V128Op.RelaxedLaneselect)) -> vecop 0x10cl + | VecTernary (V128 (I32x4 V128Op.RelaxedDotAccum)) -> vecop 0x113l + | VecTernary (V128 _) -> + error e.at "illegal ternary vector instruction" + | VecConvert (V128 (I8x16 _)) -> error e.at "illegal i8x16 conversion instruction" | VecConvert (V128 (I16x8 V128Op.ExtendLowS)) -> vecop 0x87l @@ -818,6 +865,10 @@ struct | VecConvert (V128 (I32x4 V128Op.TruncSatUF32x4)) -> vecop 0xf9l | VecConvert (V128 (I32x4 V128Op.TruncSatSZeroF64x2)) -> vecop 0xfcl | VecConvert (V128 (I32x4 V128Op.TruncSatUZeroF64x2)) -> vecop 0xfdl + | VecConvert (V128 (I32x4 V128Op.RelaxedTruncSF32x4)) -> vecop 0x101l + | VecConvert (V128 (I32x4 V128Op.RelaxedTruncUF32x4)) -> vecop 0x102l + | VecConvert (V128 (I32x4 V128Op.RelaxedTruncSZeroF64x2)) -> vecop 0x103l + | VecConvert (V128 (I32x4 V128Op.RelaxedTruncUZeroF64x2)) -> vecop 0x104l | VecConvert (V128 (I64x2 V128Op.ExtendLowS)) -> vecop 0xc7l | VecConvert (V128 (I64x2 V128Op.ExtendHighS)) -> vecop 0xc8l | VecConvert (V128 (I64x2 V128Op.ExtendLowU)) -> vecop 0xc9l @@ -948,7 +999,7 @@ struct let table tab = let {ttype; tinit} = tab.it in match ttype, tinit.it with - | TableT (_, (_, ht1)), [{it = RefNull ht2; _}] when ht1 = ht2 -> + | TableT (_, _at, (_, ht1)), [{it = RefNull ht2; _}] when ht1 = ht2 -> table_type ttype | _ -> op 0x40; op 0x00; table_type ttype; const tinit @@ -966,14 +1017,6 @@ struct section 5 (vec memory) mems (mems <> []) - (* Tag section *) - - let tag (t : tag) = byte 0x00; var t.it.tgtype - - let tag_section ts = - section 13 (vec tag) ts (ts <> []) - - (* Global section *) let global g = @@ -983,6 +1026,12 @@ struct let global_section gs = section 6 (vec global) gs (gs <> []) + (* Tag section *) + let tag (t : tag) = + byte 0x00; var t.it.tgtype + + let tag_section ts = + section 13 (vec tag) ts (ts <> []) (* Export section *) @@ -1053,7 +1102,7 @@ struct match emode.it with | Passive -> u32 0x01l; elem_kind etype; vec elem_index einit - | Active {index; offset} when index.it = 0l && is_elem_kind etype -> + | Active {index; offset} when index.it = 0l -> u32 0x00l; const offset; vec elem_index einit | Active {index; offset} -> u32 0x02l; @@ -1064,7 +1113,7 @@ struct match emode.it with | Passive -> u32 0x05l; ref_type etype; vec const einit - | Active {index; offset} when index.it = 0l && is_elem_kind etype -> + | Active {index; offset} when index.it = 0l && etype = (Null, FuncHT) -> u32 0x04l; const offset; vec const einit | Active {index; offset} -> u32 0x06l; var index; const offset; ref_type etype; vec const einit @@ -1100,40 +1149,69 @@ struct (* Custom section *) - - let custom (n, bs) = + let custom c = + let Custom.{name = n; content; _} = c.it in name n; - put_string s bs + put_string s content - let custom_section n bs = - section 0 custom (n, bs) true + let custom_section place c = + let here = Custom.(compare_place c.it.place place) <= 0 in + if here then section 0 custom c true; + here (* Module *) + let rec iterate f xs = + match xs with + | [] -> [] + | x::xs' -> if f x then iterate f xs' else xs - let module_ m = + let module_ m cs = + let open Custom in word32 0x6d736100l; word32 version; + let cs = iterate (custom_section (Before Type)) cs in type_section m.it.types; + let cs = iterate (custom_section (Before Import)) cs in import_section m.it.imports; + let cs = iterate (custom_section (Before Func)) cs in func_section m.it.funcs; + let cs = iterate (custom_section (Before Table)) cs in table_section m.it.tables; + let cs = iterate (custom_section (Before Memory)) cs in memory_section m.it.memories; + let cs = iterate (custom_section (Before Tag)) cs in tag_section m.it.tags; + let cs = iterate (custom_section (Before Global)) cs in global_section m.it.globals; + let cs = iterate (custom_section (Before Export)) cs in export_section m.it.exports; + let cs = iterate (custom_section (Before Start)) cs in start_section m.it.start; + let cs = iterate (custom_section (Before Elem)) cs in elem_section m.it.elems; + let cs = iterate (custom_section (Before DataCount)) cs in data_count_section m.it.datas m; + let cs = iterate (custom_section (Before Code)) cs in code_section m.it.funcs; - data_section m.it.datas + let cs = iterate (custom_section (Before Data)) cs in + data_section m.it.datas; + let cs = iterate (custom_section (After Data)) cs in + assert (cs = []) end +let encode_custom m bs (module S : Custom.Section) = + let open Source in + let c = S.Handler.encode m bs S.it in + Custom.{c.it with place = S.Handler.place S.it} @@ c.at + let encode m = let module E = E (struct let stream = stream () end) in - E.module_ m; to_string E.s + E.module_ m []; to_string E.s -let encode_custom name content = +let encode_with_custom (m, secs) = + let bs = encode m in let module E = E (struct let stream = stream () end) in - E.custom_section name content; to_string E.s + let cs = List.map (encode_custom m bs) secs in + E.module_ m cs; to_string E.s diff --git a/interpreter/binary/encode.mli b/interpreter/binary/encode.mli index 00fcd3164..05d1f2a67 100644 --- a/interpreter/binary/encode.mli +++ b/interpreter/binary/encode.mli @@ -2,4 +2,4 @@ exception Code of Source.region * string val version : int32 val encode : Ast.module_ -> string -val encode_custom : Ast.name -> string -> string +val encode_with_custom : Ast.module_ * Custom.section list -> string diff --git a/interpreter/custom/custom.ml b/interpreter/custom/custom.ml new file mode 100644 index 000000000..0b6a05829 --- /dev/null +++ b/interpreter/custom/custom.ml @@ -0,0 +1,86 @@ +(* Raw custom section *) + +type section_kind = + | Custom + | Type + | Import + | Func + | Table + | Memory + | Global + | Export + | Start + | Elem + | DataCount + | Code + | Data + | Tag + +type place = + | Before of section_kind + | After of section_kind + +type custom = custom' Source.phrase +and custom' = +{ + name : Ast.name; + content : string; + place : place; +} + + +let first = Type +let last = Data + +let compare_place pl1 pl2 = + match pl1, pl2 with + | Before s1, Before s2 + | After s1, After s2 -> compare s1 s2 + | Before s1, After s2 -> if s1 = s2 then -1 else compare s1 s2 + | After s1, Before s2 -> if s1 = s2 then +1 else compare s1 s2 + + +(* Handlers *) + +exception Code of Source.region * string +exception Syntax of Source.region * string +exception Invalid of Source.region * string + +module type Handler = +sig + type format' + type format = format' Source.phrase + val name : Ast.name + val place : format -> place + val decode : Ast.module_ -> string -> custom -> format (* raise Code *) + val encode : Ast.module_ -> string -> format -> custom + val parse : Ast.module_ -> string -> Annot.annot list -> format list (* raise Syntax *) + val arrange : Ast.module_ -> Sexpr.sexpr -> format -> Sexpr.sexpr + val check : Ast.module_ -> format -> unit (* raise Invalid *) +end + +module type Section = +sig + module Handler : Handler + val it : Handler.format +end + +type section = (module Section) + +let compare_section (module S1 : Section) (module S2 : Section) = + match compare_place (S1.Handler.place S1.it) (S2.Handler.place S2.it) with + | 0 -> compare S1.it.Source.at S2.it.Source.at + | n -> n + + +(* Handler registry *) + +module Registry = Map.Make(struct type t = Ast.name let compare = compare end) + +let registry = ref Registry.empty + +let register (module H : Handler) = + registry := Registry.add H.name (module H : Handler) !registry + +let handler (name : Ast.name) : (module Handler) option = + Registry.find_opt name !registry diff --git a/interpreter/custom/handler_branch_hint.ml b/interpreter/custom/handler_branch_hint.ml new file mode 100644 index 000000000..1f46d5de1 --- /dev/null +++ b/interpreter/custom/handler_branch_hint.ml @@ -0,0 +1,456 @@ +(* Handler for "metadata.code.branch_hint" section and @metadata.code.branch_hint annotations *) + +open Custom +open Annot +open Source +open Ast +module IdxMap = Map.Make (Int32) + +type kind = Likely | Unlikely + +type hint = hint' Source.phrase +and hint' = kind * int + +type hints = hint list +type func_hints = hints IdxMap.t + +type format = format' Source.phrase +and format' = { func_hints : func_hints } + +let empty = { func_hints = IdxMap.empty } +let name = Utf8.decode "metadata.code.branch_hint" +let place _fmt = Before Code +let is_contained r1 r2 = r1.left >= r2.left && r1.right <= r2.right + +let is_left r1 r2 = + r1.right.line < r2.left.line + || (r1.right.line == r2.left.line && r1.right.column <= r2.left.column) + +let starts_before r1 r2 = + r1.left.line < r2.left.line + || (r1.left.line == r2.left.line && r1.left.column < r2.left.column) + +let get_func m fidx = + let nimp = List.length m.it.imports in + let fn = Int32.to_int fidx - nimp in + List.nth m.it.funcs fn + +let flatten_instr_locs is = + let rec flatten is = + match is with + | [] -> [] + | i :: rest -> + let group = + match i.it with + | Block (_, inner) -> [ i ] @ flatten inner + | Loop (_, inner) -> [ i ] @ flatten inner + | If (_, inner1, inner2) -> [ i ] @ flatten inner1 @ flatten inner2 + | _ -> [ i ] + in + group @ flatten rest + in + let flat = flatten is in + let indexed = List.mapi (fun idx i -> (idx, i)) flat in + let sorter (_, i1) (_, i2) = if starts_before i1.at i2.at then -1 else 1 in + let sorted = List.sort sorter indexed in + sorted + +let get_inst_idx locs at = + let finder (i, l) = is_left at l.at in + let o = List.find_opt finder locs in + match o with Some (i, _) -> Some i | None -> None + +let get_nth_inst locs idx = + match List.find_opt (fun (i_idx, i) -> i_idx == idx) locs with + | Some (_, i) -> Some i + | None -> None + +(* Decoding *) + +(* TODO: make Decode module reusable instead of duplicating code *) + +type stream = { bytes : string; pos : int ref } + +exception EOS + +let stream bs = { bytes = bs; pos = ref 0 } +let len s = String.length s.bytes +let pos s = !(s.pos) +let check n s = if pos s + n > len s then raise EOS + +let skip n s = + if n < 0 then raise EOS else check n s; + s.pos := !(s.pos) + n + +let read s = Char.code s.bytes.[!(s.pos)] + +let get s = + check 1 s; + let b = read s in + skip 1 s; + b + +let position file pos = Source.{ file; line = -1; column = pos } + +let region file left right = + Source.{ left = position file left; right = position file right } + +let decode_error pos msg = + raise (Custom.Code (region "@metadata.code.branch_hint section" pos pos, msg)) + +let require b pos msg = if not b then decode_error pos msg +let decode_byte s = get s + +let rec decode_uN n s = + require (n > 0) (pos s) "integer representation too long"; + let b = decode_byte s in + require (n >= 7 || b land 0x7f < 1 lsl n) (pos s - 1) "integer too large"; + let x = Int32.of_int (b land 0x7f) in + if b land 0x80 = 0 then x + else Int32.(logor x (shift_left (decode_uN (n - 7) s) 7)) + +let decode_u32 = decode_uN 32 + +let decode_vec f s = + let n = decode_u32 s in + let n = Int32.to_int n in + let rec it i s = if i = 0 then [] else [ f s ] @ it (i - 1) s in + it n s + +let decode_hint locs foff s = + let off = decode_u32 s in + let one = decode_u32 s in + require (one = 1l) (pos s) + "@metadata.code.branch_hint section: missing reserved byte"; + let k = decode_byte s in + let hint = + match k with + | 0x00 -> Unlikely + | 0x01 -> Likely + | _ -> + decode_error (pos s) + "@metadata.code.branch_hint section: invalid hint value" + in + let abs_off = Int32.to_int (Int32.add off foff) in + let at = region "" abs_off abs_off in + let idx = + match get_inst_idx locs at with + | Some i -> i + | None -> + decode_error (pos s) + "@metadata.code.branch_hint section: invalid offset" + in + (hint, idx) @@ at + +let decode_func_hints locs foff = decode_vec (decode_hint locs foff) + +let decode_func m s = + let fidx = decode_u32 s in + let f = get_func m fidx in + let foff = Int32.of_int f.at.left.column in + let locs = flatten_instr_locs f.it.body in + let hs = decode_func_hints locs foff s in + (fidx, List.rev hs) + +let decode_funcs m s = + let fs = decode_vec (decode_func m) s in + IdxMap.add_seq (List.to_seq fs) IdxMap.empty + +let decode m _ custom = + let s = stream custom.it.content in + try { func_hints = decode_funcs m s } @@ custom.at + with EOS -> decode_error (pos s) "unexpected end of name section" + +(* Encoding *) + +(* TODO: make Encode module reusable *) + +let encode_byte buf b = Buffer.add_char buf (Char.chr b) + +let rec encode_u32 buf i = + let b = Int32.(to_int (logand i 0x7fl)) in + if 0l <= i && i < 128l then encode_byte buf b + else ( + encode_byte buf (b lor 0x80); + encode_u32 buf (Int32.shift_right_logical i 7)) + +let encode_size buf n = encode_u32 buf (Int32.of_int n) + +let encode_vec buf f v = + encode_size buf (List.length v); + let rec it v = + match v with + | [] -> () + | e :: es -> + f buf e; + it es + in + it v + +let encode_hint locs foff buf h = + let kind, idx = h.it in + let i = + match get_nth_inst locs idx with Some i -> i | None -> assert false + in + let off = i.at.left.column - foff in + encode_size buf off; + encode_u32 buf 1l; + let b = match kind with Unlikely -> 0l | Likely -> 1l in + encode_u32 buf b + +let encode_func_hints buf locs foff = encode_vec buf (encode_hint locs foff) + +let encode_func m buf t = + let fidx, hs = t in + encode_u32 buf fidx; + let f = get_func m fidx in + let foff = f.at.left.column in + let locs = flatten_instr_locs f.it.body in + encode_func_hints buf locs foff hs + +let encode_funcs buf m fhs = + encode_vec buf (encode_func m) (List.of_seq (IdxMap.to_seq fhs)) + +let encode m bs sec = + let { func_hints } = sec.it in + let m2 = Decode.decode "" bs in + let buf = Buffer.create 200 in + encode_funcs buf m2 func_hints; + let content = Buffer.contents buf in + { + name = Utf8.decode "metadata.code.branch_hint"; + content; + place = Before Code; + } + @@ sec.at + +(* Parsing *) + +open Ast + +let parse_error at msg = raise (Custom.Syntax (at, msg)) + +let merge_func_hints = + IdxMap.merge (fun key x y -> + match (x, y) with + | Some a, None -> Some a + | None, Some b -> Some b + | Some a, Some [ ({ at = _; it = _, idx } as b) ] -> + let _, last_idx = (List.hd (List.rev a)).it in + if last_idx >= idx then + parse_error b.at + "@metadata.code.branch_hint annotation: duplicate annotation" + else Some (a @ [ b ]) + | Some _, Some _ -> assert false + | None, None -> None) + +let merge s1 s2 = + { func_hints = merge_func_hints s1.it.func_hints s2.it.func_hints } + @@ { left = s1.at.left; right = s2.at.right } + +let find_func_idx m annot = + let idx = + Lib.List.index_where (fun f -> is_contained annot.at f.at) m.it.funcs + in + match idx with + | Some i -> Int32.of_int (i + List.length m.it.imports) + | None -> + parse_error annot.at + "@metadata.code.branch_hint annotation: not in a function" + +let rec parse m _bs annots = + let annots' = List.rev annots in + let ms = List.map (parse_annot m) annots' in + match ms with + | [] -> [] + | m :: ms' -> [ List.fold_left merge (empty @@ m.at) ms ] + +and parse_annot m annot = + let { name = n; items } = annot.it in + assert (n = name); + let payload a = + match a.it with + | String s -> s + | _ -> + parse_error a.at + "@metadata.code.branch_hint annotation: unexpected token" + in + let fold_payload bs a = bs ^ payload a in + let p = List.fold_left fold_payload "" items in + let at_last = (List.hd (List.rev items)).at in + let fidx = find_func_idx m annot in + let f = get_func m fidx in + let locs = flatten_instr_locs f.it.body in + let hint = + match p with + | "\x00" -> Unlikely + | "\x01" -> Likely + | _ -> + parse_error annot.at + "@metadata.code.branch_hint annotation: invalid hint value" + in + let at = Source.{ left = annot.at.left; right = at_last.right } in + let hidx = + match get_inst_idx locs at with + | Some i -> i + | None -> + parse_error annot.at + "@metadata.code.branch_hint annotation: invalid placement" + in + let e = + { func_hints = IdxMap.add fidx [ (hint, hidx) @@ at ] IdxMap.empty } + in + e @@ at + +(* Arranging *) + +let hint_to_string = function Likely -> "\"\\01\"" | Unlikely -> "\"\\00\"" + +let collect_one f hat = + let h, hidx = hat.it in + ( Int32.to_int f, + hidx, + Sexpr.Node ("@metadata.code.branch_hint ", [ Sexpr.Atom (hint_to_string h) ]) + ) + +let collect_func (f, hs) = List.map (collect_one f) hs +let collect_funcs fhs = List.concat (List.map collect_func fhs) + +let string_starts_with s1 s2 = + let s2l = String.length s2 in + let s1l = String.length s1 in + let len = Stdlib.min s2l s1l in + let s1sub = String.sub s1 0 len in + String.equal s1sub s2 + +let rec get_instrs n1 n2 = + match n2 with + | [] -> (n1, []) + | Sexpr.Atom s :: rest -> (n1 @ [ Sexpr.Atom s ], rest) + | Sexpr.Node (h, els) :: rest -> + if + string_starts_with h "type " + || String.equal h "local" + || string_starts_with h "result" + then get_instrs (n1 @ [ Sexpr.Node (h, els) ]) rest + else (n1, n2) + +let get_annot annots fidx idx h = + match !annots with + | [] -> [] + | (a_fidx, a_hidx, a_node) :: rest -> + if a_fidx = fidx && a_hidx = idx then ( + annots := rest; + [ a_node ]) + else [] + +let rec apply_instrs annots fidx curi is = + match is with + | [] -> [] + | i :: rest -> + let idx = !curi in + curi := idx + 1; + let newn = + match i with + | Sexpr.Node (h, ns) -> + let annot = get_annot annots fidx idx h in + if string_starts_with h "block" || string_starts_with h "loop" then + let pre, inner = get_instrs [] ns in + annot + @ [ Sexpr.Node (h, pre @ apply_instrs annots fidx curi inner) ] + else if string_starts_with h "if" then + match ns with + | [ Sexpr.Node (hif, nif); Sexpr.Node (helse, nelse) ] -> + let newif = apply_instrs annots fidx curi nif in + let newelse = apply_instrs annots fidx curi nelse in + annot + @ [ + Sexpr.Node + ( h, + [ + Sexpr.Node (hif, newif); Sexpr.Node (helse, newelse); + ] ); + ] + | [ + Sexpr.Node ("result", res); + Sexpr.Node (hif, nif); + Sexpr.Node (helse, nelse); + ] -> + let newif = apply_instrs annots fidx curi nif in + let newelse = apply_instrs annots fidx curi nelse in + annot + @ [ + Sexpr.Node + ( h, + [ + Sexpr.Node ("result", res); + Sexpr.Node (hif, newif); + Sexpr.Node (helse, newelse); + ] ); + ] + | _ -> assert false + else annot @ [ Sexpr.Node (h, ns) ] + | Sexpr.Atom s -> [ Sexpr.Atom s ] + in + newn @ apply_instrs annots fidx curi rest + +let apply_func nodes annots fidx = + let curi = ref 0 in + let pre, instrs = get_instrs [] nodes in + let new_instrs = apply_instrs annots fidx curi instrs in + pre @ new_instrs + +let apply_secs annots curf node = + match node with + | Sexpr.Atom a -> Sexpr.Atom a + | Sexpr.Node (head, rest) -> + if string_starts_with head "func" then ( + let ret = apply_func rest annots !curf in + curf := !curf + 1; + Sexpr.Node (head, ret)) + else Sexpr.Node (head, rest) + +let apply mnode annots curf = + match mnode with + | Sexpr.Atom a -> Sexpr.Atom a + | Sexpr.Node (h, secs) -> + Sexpr.Node (h, List.map (apply_secs annots curf) secs) + +let arrange m mnode fmt = + let annots = + ref (collect_funcs (List.of_seq (IdxMap.to_seq fmt.it.func_hints))) + in + let curf = ref 0 in + let ret = apply mnode annots curf in + ret + +(* Checking *) + +let check_error at msg = raise (Custom.Invalid (at, msg)) + +let check_one locs prev_hidx h = + let kind, idx = h.it in + match get_nth_inst locs idx with + | None -> assert false + | Some i -> ( + match i.it with + | If _ | BrIf _ -> + if !prev_hidx >= idx then + check_error h.at + "@metadata.code.branch_hint annotation: invalid order" + else ( + prev_hidx := idx; + ()) + | _ -> + check_error h.at + "@metadata.code.branch_hint annotation: invalid target") + +let check_fun m fidx hs = + let f = get_func m fidx in + let locs = flatten_instr_locs f.it.body in + let prev_hidx = ref 0 in + List.iter (check_one locs prev_hidx) hs + +let check (m : module_) (fmt : format) = + IdxMap.iter (check_fun m) fmt.it.func_hints; + () diff --git a/interpreter/custom/handler_custom.ml b/interpreter/custom/handler_custom.ml new file mode 100644 index 000000000..bcc6457ff --- /dev/null +++ b/interpreter/custom/handler_custom.ml @@ -0,0 +1,171 @@ +(* Handler for @custom annotations *) + +open Custom +open Annot +open Source + +type format' = Custom.custom' +type format = Custom.custom + +let name = Utf8.decode "custom" + +let place fmt = fmt.it.place + + +(* Decoding & encoding *) + +let decode_content m custom = + let Custom.{name; content; place} = custom.it in + match Custom.handler name with + | Some (module Handler) -> + let module S = + struct + module Handler = Handler + let it = Handler.decode m "" custom + end + in Some (module S : Custom.Section) + | None -> + if !Flags.custom_reject then + raise (Custom.Code (custom.at, + "unknown custom section \"" ^ Utf8.encode name ^ "\"")) + else + None + +let decode m _bs custom = + ignore (decode_content m custom); + custom + +let encode _m _bs custom = custom + + +(* Parsing *) + +let parse_error at msg = raise (Custom.Syntax (at, msg)) + +let rec parse m _bs annots = List.map (parse_annot m) annots + +and parse_annot m annot = + let {name = n; items} = annot.it in + assert (n = name); + let cname, items' = parse_name annot.at items in + let place, items'' = parse_place_opt items' in + let content, items''' = parse_content items'' in + parse_end items'''; + let Ast.{types; tags; globals; tables; memories; funcs; start; + elems; datas; imports; exports} = m.it in + let outside x = + if annot.at.left >= x.at.left && annot.at.right <= x.at.right then + parse_error annot.at "misplaced @custom annotation" + in + List.iter outside types; + List.iter outside tags; + List.iter outside globals; + List.iter outside tables; + List.iter outside memories; + List.iter outside funcs; + List.iter outside (Option.to_list start); + List.iter outside elems; + List.iter outside datas; + List.iter outside imports; + List.iter outside exports; + let custom = {name = cname; content; place} @@ annot.at in + ignore (decode_content m custom); + custom + +and parse_name at = function + | {it = String s; at} :: items -> + (try Utf8.decode s, items with Utf8.Utf8 -> + parse_error at "@custom annotation: malformed UTF-8 encoding" + ) + | _ -> + parse_error at "@custom annotation: missing section name" + +and parse_place_opt = function + | {it = Parens items'; at} :: items -> + let dir, items'' = parse_direction at items' in + let sec, items''' = parse_section at items'' in + parse_end items'''; + dir sec, items + | items -> + After last, items + +and parse_direction at = function + | {it = Atom "before"; _} :: items -> (fun sec -> Before sec), items + | {it = Atom "after"; _} :: items -> (fun sec -> After sec), items + | _ -> + parse_error at "@custom annotation: malformed placement" + +and parse_section at = function + | {it = Atom "type"; _} :: items -> Type, items + | {it = Atom "tag"; _} :: items -> Tag, items + | {it = Atom "import"; _} :: items -> Import, items + | {it = Atom "func"; _} :: items -> Func, items + | {it = Atom "table"; _} :: items -> Table, items + | {it = Atom "memory"; _} :: items -> Memory, items + | {it = Atom "global"; _} :: items -> Global, items + | {it = Atom "export"; _} :: items -> Export, items + | {it = Atom "start"; _} :: items -> Start, items + | {it = Atom "elem"; _} :: items -> Elem, items + | {it = Atom "code"; _} :: items -> Code, items + | {it = Atom "data"; _} :: items -> Data, items + | {it = Atom "datacount"; _} :: items -> DataCount, items + | {it = Atom "first"; _} :: items -> first, items + | {it = Atom "last"; _} :: items -> last, items + | _ -> + parse_error at "@custom annotation: malformed section kind" + +and parse_content = function + | {it = String bs; _} :: items -> + let bs', items' = parse_content items in + bs ^ bs', items' + | items -> "", items + +and parse_end = function + | [] -> () + | item :: _ -> + parse_error item.at "@custom annotation: unexpected token" + + +(* Printing *) + +open Sexpr + +let rec arrange _m mnode custom = + let {name; content; place} = custom.it in + let node = Node ("@custom " ^ Arrange.name name, + arrange_place place :: Arrange.break_bytes content + ) in + match mnode with + | Sexpr.Atom _ -> assert false + | Node (name, secs) -> Node (name, secs @ [node]) + +and arrange_place = function + | Before sec -> Node ("before", [Atom (arrange_sec sec)]) + | After sec -> Node ("after", [Atom (arrange_sec sec)]) + +and arrange_sec = function + | Custom -> assert false + | Type -> "type" + | Tag -> "tag" + | Import -> "import" + | Func -> "func" + | Table -> "table" + | Memory -> "memory" + | Global -> "global" + | Export -> "export" + | Start -> "start" + | Elem -> "elem" + | Code -> "code" + | Data -> "data" + | DataCount -> "datacount" + + +(* Checking *) + +let check m custom = + let {place; _} = custom.it in + assert (compare_place place (After Custom) > 0); + match decode_content m custom with + | None -> () + | Some (module S : Custom.Section) -> + S.Handler.check m S.it diff --git a/interpreter/custom/handler_custom.mli b/interpreter/custom/handler_custom.mli new file mode 100644 index 000000000..618ad1e4b --- /dev/null +++ b/interpreter/custom/handler_custom.mli @@ -0,0 +1 @@ +include Custom.Handler with type format' = Custom.custom' diff --git a/interpreter/custom/handler_name.ml b/interpreter/custom/handler_name.ml new file mode 100644 index 000000000..dde5e28d7 --- /dev/null +++ b/interpreter/custom/handler_name.ml @@ -0,0 +1,454 @@ +(* Handler for "name" section and @name annotations *) + +open Custom +open Annot +open Source + +module IdxMap = Map.Make(Int32) + +type name = Ast.name Source.phrase +type name_map = name IdxMap.t +type indirect_name_map = name_map Source.phrase IdxMap.t + +type format = format' Source.phrase +and format' = +{ + module_ : name option; + funcs : name_map; + locals : indirect_name_map; + types : name_map; + fields : indirect_name_map; + tags : name_map; +} + + +let empty = +{ + module_ = None; + funcs = IdxMap.empty; + locals = IdxMap.empty; + types = IdxMap.empty; + fields = IdxMap.empty; + tags = IdxMap.empty; +} + +let name = Utf8.decode "name" + +let place _fmt = After last + + +(* Decoding *) + +(* TODO: make Decode module reusable instead of duplicating code *) + +type stream = {bytes : string; pos : int ref} + +exception EOS + +let stream bs = {bytes = bs; pos = ref 0} + +let len s = String.length s.bytes +let pos s = !(s.pos) +let eos s = (pos s = len s) + +let check n s = if pos s + n > len s then raise EOS +let skip n s = if n < 0 then raise EOS else check n s; s.pos := !(s.pos) + n + +let read s = Char.code (s.bytes.[!(s.pos)]) +let peek s = if eos s then None else Some (read s) +let get s = check 1 s; let b = read s in skip 1 s; b +let get_string n s = let i = pos s in skip n s; String.sub s.bytes i n + +let position pos = Source.{file = "@name section"; line = -1; column = pos} +let region left right = Source.{left = position left; right = position right} + +let at f s = + let left = pos s in + let x = f s in + let right = pos s in + Source.(x @@ region left right) + +let decode_error pos msg = raise (Custom.Code (region pos pos, msg)) +let require b pos msg = if not b then decode_error pos msg + +let decode_byte s = + get s + +let rec decode_uN n s = + require (n > 0) (pos s) "integer representation too long"; + let b = decode_byte s in + require (n >= 7 || b land 0x7f < 1 lsl n) (pos s - 1) "integer too large"; + let x = Int32.of_int (b land 0x7f) in + if b land 0x80 = 0 then x else + Int32.(logor x (shift_left (decode_uN (n - 7) s) 7)) + +let decode_u32 = decode_uN 32 + +let decode_size s = + Int32.to_int (decode_u32 s) + +let decode_name s = + let n = decode_size s in + let pos = pos s in + try Utf8.decode (get_string n s) with Utf8.Utf8 -> + decode_error pos "malformed UTF-8 encoding" + +let decode_name_assoc s = + let x = decode_u32 s in + let n = decode_name s in + (x, n) + +let decode_name_map s = + let n = decode_size s in + let m = ref IdxMap.empty in + for _ = 1 to n do + let {it = (x, name); at} = at decode_name_assoc s in + if IdxMap.mem x !m then + decode_error at.left.column "custom @name: multiple function or local names"; + m := IdxMap.add x (name @@ at) !m + done; + !m + +let decode_indirect_name_assoc s = + let x = decode_u32 s in + let m = at decode_name_map s in + (x, m) + +let decode_indirect_name_map s = + let n = decode_size s in + let m = ref IdxMap.empty in + for _ = 1 to n do + let {it = (x, m'); at} = at decode_indirect_name_assoc s in + if IdxMap.mem x !m then + decode_error at.left.column "custom @name: multiple function names"; + m := IdxMap.add x m' !m + done; + !m + +let decode_module s = Some (at decode_name s) +let decode_funcs s = decode_name_map s +let decode_locals s = decode_indirect_name_map s +let decode_types s = decode_name_map s +let decode_fields s = decode_indirect_name_map s +let decode_tags s = decode_name_map s + +let decode_subsec id f default s = + match peek s with + | None -> default + | Some id' when id' <> id -> default + | _ -> + let _id = decode_byte s in + let n = decode_size s in + let pos' = pos s in + let ss = f s in + require (pos s = pos' + n) (pos s) "name subsection size mismatch"; + ss + +let decode _m _bs custom = + let s = stream custom.it.content in + try + let module_ = decode_subsec 0 decode_module None s in + let funcs = decode_subsec 1 decode_funcs IdxMap.empty s in + let locals = decode_subsec 2 decode_locals IdxMap.empty s in + let types = decode_subsec 4 decode_types IdxMap.empty s in + let fields = decode_subsec 10 decode_fields IdxMap.empty s in + let tags = decode_subsec 11 decode_tags IdxMap.empty s in + require (eos s) (pos s) "invalid name subsection id"; + {module_; funcs; locals; types; fields; tags} @@ custom.at + with EOS -> decode_error (pos s) "unexpected end of name section" + + +(* Encoding *) + +(* TODO: make Encode module reusable *) + +let encode_byte buf b = + Buffer.add_char buf (Char.chr b) + +let rec encode_u32 buf i = + let b = Int32.(to_int (logand i 0x7fl)) in + if 0l <= i && i < 128l then encode_byte buf b + else ( + encode_byte buf (b lor 0x80); + encode_u32 buf (Int32.shift_right_logical i 7) + ) + +let encode_size buf n = + encode_u32 buf (Int32.of_int n) + +let encode_name buf n = + let s = Utf8.encode n in + encode_size buf (String.length s); + Buffer.add_string buf s + +let encode_name_assoc buf x n = + encode_u32 buf x; + encode_name buf n.it + +let encode_name_map buf m = + encode_size buf (IdxMap.cardinal m); + IdxMap.iter (encode_name_assoc buf) m + +let encode_indirect_name_assoc buf x m = + encode_u32 buf x; + encode_name_map buf m.it + +let encode_indirect_name_map buf m = + encode_size buf (IdxMap.cardinal m); + IdxMap.iter (encode_indirect_name_assoc buf) m + +let encode_subsec_begin buf id = + encode_byte buf id; + let pre = Buffer.contents buf in + Buffer.clear buf; + pre + +let encode_subsec_end buf pre = + let contents = Buffer.contents buf in + Buffer.clear buf; + Buffer.add_string buf pre; + encode_size buf (String.length contents); + Buffer.add_string buf contents + +let encode_module buf name_opt = + match name_opt with + | None -> () + | Some name -> + let subsec = encode_subsec_begin buf 0 in + encode_name buf name.it; + encode_subsec_end buf subsec + +let encode_funcs buf name_map = + if not (IdxMap.is_empty name_map) then begin + let subsec = encode_subsec_begin buf 1 in + encode_name_map buf name_map; + encode_subsec_end buf subsec + end + +let encode_locals buf name_map_map = + if not (IdxMap.is_empty name_map_map) then begin + let subsec = encode_subsec_begin buf 2 in + encode_indirect_name_map buf name_map_map; + encode_subsec_end buf subsec + end + +let encode_types buf name_map = + if not (IdxMap.is_empty name_map) then begin + let subsec = encode_subsec_begin buf 4 in + encode_name_map buf name_map; + encode_subsec_end buf subsec + end + +let encode_fields buf name_map_map = + if not (IdxMap.is_empty name_map_map) then begin + let subsec = encode_subsec_begin buf 10 in + encode_indirect_name_map buf name_map_map; + encode_subsec_end buf subsec + end + +let encode_tags buf name_map = + if not (IdxMap.is_empty name_map) then begin + let subsec = encode_subsec_begin buf 11 in + encode_name_map buf name_map; + encode_subsec_end buf subsec + end + +let encode _m _bs sec = + let {module_; funcs; locals; types; fields; tags} = sec.it in + let buf = Buffer.create 200 in + encode_module buf module_; + encode_funcs buf funcs; + encode_locals buf locals; + encode_types buf types; + encode_fields buf fields; + encode_tags buf tags; + let content = Buffer.contents buf in + {name = Utf8.decode "name"; content; place = After last} @@ sec.at + + +(* Parsing *) + +open Ast +open Types + +let parse_error at msg = raise (Custom.Syntax (at, msg)) + +let merge_name_opt n1 n2 = + match n1, n2 with + | None, None -> None + | None, some + | some, None -> some + | Some _, Some n2 -> + parse_error n2.at "@name annotation: multiple module names" + +let merge_name_map m1 m2 = + IdxMap.union (fun x _ n2 -> + parse_error n2.at "@name annotation: multiple function names" + ) m1 m2 + +let merge_indirect_name_map m1 m2 = + IdxMap.union (fun x m1' m2' -> + Some ( + IdxMap.union (fun x _ n2 -> + parse_error n2.at "@name annotation: multiple local names" + ) m1'.it m2'.it @@ {left = m1'.at.left; right = m2'.at.right} + ) + ) m1 m2 + +let merge s1 s2 = + { + module_ = merge_name_opt s1.it.module_ s2.it.module_; + funcs = merge_name_map s1.it.funcs s2.it.funcs; + locals = merge_indirect_name_map s1.it.locals s2.it.locals; + types = merge_name_map s1.it.types s2.it.types; + fields = merge_indirect_name_map s1.it.fields s2.it.fields; + tags = merge_name_map s1.it.tags s2.it.tags; + } @@ {left = s1.at.left; right = s2.at.right} + + +let is_contained r1 r2 = r1.left >= r2.left && r1.right <= r2.right +let is_left r1 r2 = r1.right <= r2.left + +let locate_func bs x name at (f : func) = + if is_left at f.it.ftype.at then + {empty with funcs = IdxMap.singleton x name} + else if f.it.body = [] || is_left at (List.hd f.it.body).at then + (* TODO re-parse the function params and locals from bs *) + parse_error at "@name annotation: local names not yet supported" + else + parse_error at "@name annotation: misplaced annotation" + +let locate_tag bs x name at (tag : tag) = + if is_left at tag.it.tgtype.at then + {empty with tags = IdxMap.singleton x name} + else + parse_error at "@name annotation: misplaced annotation" + +let locate_type bs x name at (ty : type_) = + (* TODO re-parse types from bs *) + parse_error at "@name annotation: type and field names not yet supported" + +let locate_module bs name at (m : module_) = + if not (is_contained at m.at) then + parse_error at "misplaced @name annotation"; + let {types; tags; globals; tables; memories; funcs; start; + elems; datas; imports; exports} = m.it in + let ats = + List.map (fun p -> p.at) types @ + List.map (fun p -> p.at) tags @ + List.map (fun p -> p.at) globals @ + List.map (fun p -> p.at) tables @ + List.map (fun p -> p.at) memories @ + List.map (fun p -> p.at) funcs @ + List.map (fun p -> p.at) (Option.to_list start) @ + List.map (fun p -> p.at) elems @ + List.map (fun p -> p.at) datas @ + List.map (fun p -> p.at) imports @ + List.map (fun p -> p.at) exports |> List.sort compare + in + match ats with + | [] -> {empty with module_ = Some name} + | at1::_ when is_left at at1 -> {empty with module_ = Some name} + | _ -> + match Lib.List.index_where (fun t -> is_contained at t.at) types with + | Some x -> locate_type bs (Int32.of_int x) name at (List.nth types x) + | None -> + match Lib.List.index_where (fun t -> is_contained at t.at) tags with + | Some x -> locate_tag bs (Int32.of_int x) name at (List.nth tags x) + | None -> + match Lib.List.index_where (fun f -> is_contained at f.at) funcs with + | Some x -> locate_func bs (Int32.of_int x) name at (List.nth funcs x) + | None -> parse_error at "misplaced @name annotation" + + +let rec parse m bs annots = + let ms = List.map (parse_annot m bs) annots in + match ms with + | [] -> [] + | m::ms' -> [List.fold_left merge (empty @@ m.at) ms] + +and parse_annot m bs annot = + let {name = n; items} = annot.it in + assert (n = name); + let name, items' = parse_name annot.at items in + parse_end items'; + locate_module bs name annot.at m @@ annot.at + +and parse_name at = function + | {it = String s; at} :: items -> + (try Utf8.decode s @@ at, items with Utf8.Utf8 -> + parse_error at "malformed UTF-8 encoding" + ) + | _ -> + parse_error at "@name annotation: string expected" + +and parse_end = function + | [] -> () + | item :: _ -> + parse_error item.at "@name annotation: unexpected token" + + +(* Printing *) + +let arrange m bs fmt = + (* Print as generic custom section *) + Handler_custom.arrange m bs (encode m "" fmt) + + +(* Checking *) + +let check_error at msg = raise (Custom.Invalid (at, msg)) + +let check (m : module_) (fmt : format) = + let subtypes = + List.concat (List.map (fun {it = RecT ss; _} -> ss) m.it.types) in + let comptypes = List.map (fun (SubT (_, _, ct)) -> ct) subtypes in + IdxMap.iter (fun x name -> + if I32.ge_u x (Lib.List32.length m.it.funcs) then + check_error name.at ("custom @name: invalid function index " ^ + I32.to_string_u x) + ) fmt.it.funcs; + IdxMap.iter (fun x map -> + if I32.ge_u x (Lib.List32.length m.it.funcs) then + check_error map.at ("custom @name: invalid function index " ^ + I32.to_string_u x); + let f = Lib.List32.nth m.it.funcs x in + if I32.ge_u f.it.ftype.it (Lib.List32.length comptypes) then + check_error map.at ("custom @name: invalid type index " ^ + I32.to_string_u f.it.ftype.it ^ " for function " ^ I32.to_string_u x); + let ts = + match Lib.List32.nth comptypes f.it.ftype.it with + | DefFuncT (FuncT (ts, _)) -> ts + | _ -> + check_error map.at ("custom @name: non-function type " ^ + I32.to_string_u f.it.ftype.it ^ " for function " ^ I32.to_string_u x) + in + let n = I32.add (Lib.List32.length ts) (Lib.List32.length f.it.locals) in + IdxMap.iter (fun y name -> + if I32.ge_u y n then + check_error name.at ("custom @name: invalid local index " ^ + I32.to_string_u y ^ " for function " ^ I32.to_string_u x) + ) map.it; + ) fmt.it.locals; + IdxMap.iter (fun x name -> + if I32.ge_u x (Lib.List32.length comptypes) then + check_error name.at ("custom @name: invalid type index " ^ + I32.to_string_u x) + ) fmt.it.types; + IdxMap.iter (fun x map -> + if I32.ge_u x (Lib.List32.length comptypes) then + check_error map.at ("custom @name: invalid type index " ^ + I32.to_string_u x); + let n = + match Lib.List32.nth comptypes x with + | DefStructT (StructT fs) -> Lib.List32.length fs + | _ -> + check_error map.at ("custom @name: non-struct type " ^ + I32.to_string_u x) + in + IdxMap.iter (fun y name -> + if I32.ge_u y n then + check_error name.at ("custom @name: invalid field index " ^ + I32.to_string_u y ^ " for type " ^ I32.to_string_u x) + ) map.it; + ) fmt.it.fields diff --git a/interpreter/custom/handler_name.mli b/interpreter/custom/handler_name.mli new file mode 100644 index 000000000..caccb08bc --- /dev/null +++ b/interpreter/custom/handler_name.mli @@ -0,0 +1 @@ +include Custom.Handler diff --git a/interpreter/exec/eval.ml b/interpreter/exec/eval.ml index f68e20703..32b80606a 100644 --- a/interpreter/exec/eval.ml +++ b/interpreter/exec/eval.ml @@ -9,16 +9,18 @@ open Instance (* Errors *) module Link = Error.Make () -module Exception = Error.Make () module Trap = Error.Make () -module Crash = Error.Make () +module Exception = Error.Make () +module Suspension = Error.Make () module Exhaustion = Error.Make () +module Crash = Error.Make () exception Link = Link.Error -exception Exception = Exception.Error exception Trap = Trap.Error -exception Crash = Crash.Error (* failure that cannot happen in valid code *) +exception Exception = Exception.Error +exception Suspension = Suspension.Error exception Exhaustion = Exhaustion.Error +exception Crash = Crash.Error (* failure that cannot happen in valid code *) let table_error at = function | Table.Bounds -> "out of bounds table access" @@ -45,7 +47,7 @@ let numeric_error at = function | exn -> raise exn -(* Administrative Expressions & Configurations *) +(* Administrative Expressions & Continuations *) type 'a stack = 'a list @@ -70,6 +72,40 @@ and admin_instr' = | Label of int * instr list * code | Frame of int * frame * code | Handler of int * catch list * code + | Prompt of handle_table * code + | Suspending of tag_inst * value stack * (int32 * ref_) option * ctxt + +and ctxt = code -> code +and handle_table = (tag_inst * idx) list * tag_inst list + +type cont = int32 * ctxt (* TODO: represent type properly *) +type ref_ += ContRef of cont option ref + +let () = + let type_of_ref' = !Value.type_of_ref' in + Value.type_of_ref' := function + | ContRef _ -> ContHT + | r -> type_of_ref' r + +let () = + let string_of_ref' = !Value.string_of_ref' in + Value.string_of_ref' := function + | ContRef _ -> "cont" + | r -> string_of_ref' r + +let plain e = Plain e.it @@ e.at + +let is_jumping e = + match e.it with + | Returning _ | ReturningInvoke _ | Breaking _ + | Throwing _ | Trapping _ | Suspending _ -> true + | _ -> false + + +let compose (vs1, es1) (vs2, es2) = vs1 @ vs2, es1 @ es2 + + +(* Configurations *) type config = { @@ -82,20 +118,12 @@ let frame inst locals = {inst; locals} let config inst vs es = {frame = frame inst []; code = vs, es; budget = !Flags.budget} -let plain e = Plain e.it @@ e.at - let admin_instr_of_value (v : value) at : admin_instr' = match v with | Num n -> Plain (Const (n @@ at)) | Vec v -> Plain (VecConst (v @@ at)) | Ref r -> Refer r -let is_jumping e = - match e.it with - | Returning _ | ReturningInvoke _ | Breaking _ - | Throwing _ | Trapping _ -> true - | _ -> false - let lookup category list x = try Lib.List32.nth list x.it with Failure _ -> Crash.error x.at ("undefined " ^ category ^ " " ^ Int32.to_string x.it) @@ -104,8 +132,8 @@ let type_ (inst : module_inst) x = lookup "type" inst.types x let func (inst : module_inst) x = lookup "function" inst.funcs x let table (inst : module_inst) x = lookup "table" inst.tables x let memory (inst : module_inst) x = lookup "memory" inst.memories x -let tag (inst : module_inst) x = lookup "tag" inst.tags x let global (inst : module_inst) x = lookup "global" inst.globals x +let tag (inst : module_inst) x = lookup "tag" inst.tags x let elem (inst : module_inst) x = lookup "element segment" inst.elems x let data (inst : module_inst) x = lookup "data segment" inst.datas x let local (frame : frame) x = lookup "local" frame.locals x @@ -114,6 +142,7 @@ let str_type (inst : module_inst) x = expand_def_type (type_ inst x) let func_type (inst : module_inst) x = as_func_str_type (str_type inst x) let struct_type (inst : module_inst) x = as_struct_str_type (str_type inst x) let array_type (inst : module_inst) x = as_array_str_type (str_type inst x) +let cont_type (inst : module_inst) x = as_cont_str_type (str_type inst x) let subst_of (inst : module_inst) = function | StatX x when x < Lib.List32.length inst.types -> @@ -122,13 +151,13 @@ let subst_of (inst : module_inst) = function let any_ref (inst : module_inst) x i at = try Table.load (table inst x) i with Table.Bounds -> - Trap.error at ("undefined element " ^ Int32.to_string i) + Trap.error at ("undefined element " ^ Int64.to_string i) let func_ref (inst : module_inst) x i at = match any_ref inst x i at with | FuncRef f -> f - | NullRef _ -> Trap.error at ("uninitialized element " ^ Int32.to_string i) - | _ -> Crash.error at ("type mismatch for element " ^ Int32.to_string i) + | NullRef _ -> Trap.error at ("uninitialized element " ^ Int64.to_string i) + | _ -> Crash.error at ("type mismatch for element " ^ Int64.to_string i) let block_type (inst : module_inst) bt at = match bt with @@ -145,6 +174,24 @@ let drop n (vs : 'a stack) at = let split n (vs : 'a stack) at = take n vs at, drop n vs at +let i32_split n (vs : 'a stack) at = + try + Lib.List32.take n vs, Lib.List32.drop n vs + with + Failure _ -> Crash.error at "stack underflow" + +let str_type_of_heap_type (inst : module_inst) ht : str_type = + match ht with + | VarHT (StatX x) -> str_type inst (x @@ Source.no_region) + | DefHT dt -> expand_def_type dt + | _ -> assert false + +let func_type_of_cont_type (inst : module_inst) (ContT ht) : func_type = + as_func_str_type (str_type_of_heap_type inst ht) + +let func_type_of_tag_type (_inst : module_inst) (TagT dt) : func_type = + as_func_str_type (expand_def_type dt) + (* Evaluation *) @@ -158,25 +205,44 @@ let split n (vs : 'a stack) at = take n vs at, drop n vs at *) let mem_oob frame x i n = - I64.gt_u (I64.add (I64_convert.extend_i32_u i) (I64_convert.extend_i32_u n)) - (Memory.bound (memory frame.inst x)) + let mem = (memory frame.inst x) in + I64.gt_u (I64.add (addr_of_num i) (addr_of_num n)) + (Memory.bound mem) let data_oob frame x i n = - I64.gt_u (I64.add (I64_convert.extend_i32_u i) (I64_convert.extend_i32_u n)) + I64.gt_u (I64.add (addr_of_num i) (addr_of_num n)) (Data.size (data frame.inst x)) let table_oob frame x i n = - I64.gt_u (I64.add (I64_convert.extend_i32_u i) (I64_convert.extend_i32_u n)) - (I64_convert.extend_i32_u (Table.size (table frame.inst x))) + I64.gt_u (I64.add (addr_of_num i) (addr_of_num n)) + (Table.size (table frame.inst x)) let elem_oob frame x i n = - I64.gt_u (I64.add (I64_convert.extend_i32_u i) (I64_convert.extend_i32_u n)) - (I64_convert.extend_i32_u (Elem.size (elem frame.inst x))) + I64.gt_u (I64.add (addr_of_num i) (addr_of_num n)) + (Elem.size (elem frame.inst x)) let array_oob a i n = I64.gt_u (I64.add (I64_convert.extend_i32_u i) (I64_convert.extend_i32_u n)) (I64_convert.extend_i32_u (Aggr.array_length a)) +let handle_table (c : config) xls : handle_table = + let suspend = + List.filter_map + (fun (x, hdl) -> + match hdl with + | OnLabel l -> Some (tag c.frame.inst x, l) + | _ -> None) + xls + in + let switch = + List.filter_map + (fun (x, hdl) -> + match hdl with + | OnSwitch -> Some (tag c.frame.inst x) + | _ -> None) + xls + in + (suspend, switch) let rec step (c : config) : config = let vs, es = c.code in @@ -185,7 +251,7 @@ let rec step (c : config) : config = match e.it, vs with | Plain e', vs -> (match e', vs with - | Unreachable, vs -> + | Unreachable, vs -> vs, [Trapping "unreachable executed" @@ e.at] | Nop, vs -> @@ -263,8 +329,9 @@ let rec step (c : config) : config = | CallRef _x, Ref (FuncRef f) :: vs -> vs, [Invoke f @@ e.at] - | CallIndirect (x, y), Num (I32 i) :: vs -> - let f = func_ref c.frame.inst x i e.at in + | CallIndirect (x, y), Num i :: vs -> + let i_64 = addr_of_num i in + let f = func_ref c.frame.inst x i_64 e.at in if Match.match_def_type [] (Func.type_of f) (type_ c.frame.inst y) then vs, [Invoke f @@ e.at] else @@ -272,12 +339,6 @@ let rec step (c : config) : config = string_of_def_type (type_ c.frame.inst y) ^ " but got " ^ string_of_def_type (Func.type_of f)) @@ e.at] - | ReturnCall x, vs -> - (match (step {c with code = (vs, [Plain (Call x) @@ e.at])}).code with - | vs', [{it = Invoke a; at}] -> vs', [ReturningInvoke (vs', a) @@ at] - | _ -> assert false - ) - | ReturnCallRef _x, Ref (NullRef _) :: vs -> vs, [Trapping "null function reference" @@ e.at] @@ -288,6 +349,100 @@ let rec step (c : config) : config = | _ -> assert false ) + | ContNew x, Ref (NullRef _) :: vs -> + vs, [Trapping "null function reference" @@ e.at] + + | ContNew x, Ref (FuncRef f) :: vs -> + let FuncT (ts, _) = as_func_str_type (expand_def_type (Func.type_of f)) in + let ctxt code = compose code ([], [Invoke f @@ e.at]) in + Ref (ContRef (ref (Some (Lib.List32.length ts, ctxt)))) :: vs, [] + + | ContBind (x, y), Ref (NullRef _) :: vs -> + vs, [Trapping "null continuation reference" @@ e.at] + + | ContBind (x, y), Ref (ContRef {contents = None}) :: vs -> + vs, [Trapping "continuation already consumed" @@ e.at] + + | ContBind (x, y), Ref (ContRef ({contents = Some (n, ctxt)} as cont)) :: vs -> + let ct = cont_type c.frame.inst y in + let ct = subst_cont_type (subst_of c.frame.inst) ct in + let FuncT (ts', _) = func_type_of_cont_type c.frame.inst ct in + let args, vs' = + try i32_split (I32.sub n (Lib.List32.length ts')) vs e.at + with Failure _ -> Crash.error e.at "type mismatch at continuation bind" + in + cont := None; + let ctxt' code = ctxt (compose code (args, [])) in + Ref (ContRef (ref (Some (I32.sub n (Lib.List32.length args), ctxt')))) :: vs', [] + + | Suspend x, vs -> + let tagt = tag c.frame.inst x in + let FuncT (ts, _) = func_type_of_tag_type c.frame.inst (Tag.type_of tagt) in + let args, vs' = i32_split (Lib.List32.length ts) vs e.at in + vs', [Suspending (tagt, args, None, fun code -> code) @@ e.at] + + | Resume (x, xls), Ref (NullRef _) :: vs -> + vs, [Trapping "null continuation reference" @@ e.at] + + | Resume (x, xls), Ref (ContRef {contents = None}) :: vs -> + vs, [Trapping "continuation already consumed" @@ e.at] + + | Resume (x, xls), Ref (ContRef ({contents = Some (n, ctxt)} as cont)) :: vs -> + let hs = handle_table c xls in + let args, vs' = i32_split n vs e.at in + cont := None; + vs', [Prompt (hs, ctxt (args, [])) @@ e.at] + + | ResumeThrow (x, y, xls), Ref (NullRef _) :: vs -> + vs, [Trapping "null continuation reference" @@ e.at] + + | ResumeThrow (x, y, xls), Ref (ContRef {contents = None}) :: vs -> + vs, [Trapping "continuation already consumed" @@ e.at] + + | ResumeThrow (x, y, xls), Ref (ContRef ({contents = Some (n, ctxt)} as cont)) :: vs -> + let tagt = tag c.frame.inst y in + let FuncT (ts, _) = func_type_of_tag_type c.frame.inst (Tag.type_of tagt) in + let hs = handle_table c xls in + let args, vs' = i32_split (Lib.List32.length ts) vs e.at in + cont := None; + vs', [Prompt (hs, ctxt ([], [Throwing (tagt, args) @@ e.at])) @@ e.at] + + | ResumeThrowRef (x, xls), Ref _ :: Ref (NullRef _) :: vs -> + vs, [Trapping "null exception reference" @@ e.at] + + | ResumeThrowRef (x, xls), Ref (NullRef _) :: vs -> + vs, [Trapping "null continuation reference" @@ e.at] + + | ResumeThrowRef (x, xls), Ref (ContRef {contents = None}) :: Ref _ :: vs -> + vs, [Trapping "continuation already consumed" @@ e.at] + + | ResumeThrowRef (x, xls), + Ref (ContRef ({contents = Some (n, ctxt)} as cont)) :: + v :: vs -> + let hs = handle_table c xls in + cont := None; + vs, [Prompt (hs, ctxt ([v], [Plain ThrowRef @@ e.at])) @@ e.at] + + | Switch (x, y), Ref (NullRef _) :: vs -> + vs, [Trapping "null continuation reference" @@ e.at] + + | Switch (x, y), Ref (ContRef {contents = None}) :: vs -> + vs, [Trapping "continuation already consumed" @@ e.at] + + | Switch (x, y), Ref (ContRef ({contents = Some (n, ctxt)} as cont)) :: vs -> + let FuncT (ts, _) = func_type_of_cont_type c.frame.inst (cont_type c.frame.inst x) in + let FuncT (ts', _) = as_cont_func_ref_type (Lib.List.last ts) in + let arity = Lib.List32.length ts' in + let tagt = tag c.frame.inst y in + let args, vs' = i32_split (Int32.sub n 1l) vs e.at in + vs', [Suspending (tagt, args, Some (arity, ContRef cont), fun code -> code) @@ e.at] + + | ReturnCall x, vs -> + (match (step {c with code = (vs, [Plain (Call x) @@ e.at])}).code with + | vs', [{it = Invoke a; at}] -> vs', [ReturningInvoke (vs', a) @@ at] + | _ -> assert false + ) + | ReturnCallIndirect (x, y), vs -> (match (step {c with code = (vs, [Plain (CallIndirect (x, y)) @@ e.at])}).code @@ -299,8 +454,7 @@ let rec step (c : config) : config = | Throw x, vs -> let t = tag c.frame.inst x in - let TagT dt = Tag.type_of t in - let FuncT (ts, _) = as_func_str_type (expand_def_type dt) in + let FuncT (ts, _) = func_type_of_tag_type c.frame.inst (Tag.type_of t) in let n = List.length ts in let args, vs' = split n vs e.at in vs', [Throwing (t, args) @@ e.at] @@ -308,7 +462,7 @@ let rec step (c : config) : config = | ThrowRef, Ref (NullRef _) :: vs -> vs, [Trapping "null exception reference" @@ e.at] - | ThrowRef, Ref (ExnRef (t, args)) :: vs -> + | ThrowRef, Ref (Exn.(ExnRef (Exn (t, args)))) :: vs -> vs, [Throwing (t, args) @@ e.at] | TryTable (bt, cs, es'), vs -> @@ -316,7 +470,7 @@ let rec step (c : config) : config = let n1 = List.length ts1 in let n2 = List.length ts2 in let args, vs' = split n1 vs e.at in - vs', [Handler (n2, cs, (args, [Label (n2, [], ([], List.map plain es')) @@ e.at])) @@ e.at] + vs', [Handler (n2, cs, ([], [Label (n2, [], (args, List.map plain es')) @@ e.at])) @@ e.at] | Drop, v :: vs' -> vs', [] @@ -351,85 +505,96 @@ let rec step (c : config) : config = with Global.NotMutable -> Crash.error e.at "write to immutable global" | Global.Type -> Crash.error e.at "type mismatch at global write") - | TableGet x, Num (I32 i) :: vs' -> - (try Ref (Table.load (table c.frame.inst x) i) :: vs', [] + | TableGet x, Num i :: vs' -> + let i_64 = addr_of_num i in + (try Ref (Table.load (table c.frame.inst x) i_64) :: vs', [] with exn -> vs', [Trapping (table_error e.at exn) @@ e.at]) - | TableSet x, Ref r :: Num (I32 i) :: vs' -> - (try Table.store (table c.frame.inst x) i r; vs', [] + | TableSet x, Ref r :: Num i :: vs' -> + let i_64 = addr_of_num i in + (try Table.store (table c.frame.inst x) i_64 r; vs', [] with exn -> vs', [Trapping (table_error e.at exn) @@ e.at]) | TableSize x, vs -> - Num (I32 (Table.size (table c.frame.inst x))) :: vs, [] + let tab = table c.frame.inst x in + Num (num_of_addr (Table.addr_type_of tab) (Table.size tab)) :: vs, [] - | TableGrow x, Num (I32 delta) :: Ref r :: vs' -> + | TableGrow x, Num n :: Ref r :: vs' -> + let n_64 = addr_of_num n in let tab = table c.frame.inst x in let old_size = Table.size tab in let result = - try Table.grow tab delta r; old_size - with Table.SizeOverflow | Table.SizeLimit | Table.OutOfMemory -> -1l - in Num (I32 result) :: vs', [] + try Table.grow tab n_64 r; old_size + with Table.SizeOverflow | Table.SizeLimit | Table.OutOfMemory -> -1L + in Num (num_of_addr (Table.addr_type_of tab) result) :: vs', [] - | TableFill x, Num (I32 n) :: Ref r :: Num (I32 i) :: vs' -> + | TableFill x, Num n :: Ref r :: Num i :: vs' -> + let n_64 = addr_of_num n in + let i_64 = addr_of_num i in if table_oob c.frame x i n then vs', [Trapping (table_error e.at Table.Bounds) @@ e.at] - else if n = 0l then + else if n_64 = 0L then vs', [] else - let _ = assert (I32.lt_u i 0xffff_ffffl) in + let _ = assert (I64.lt_u i_64 0xffff_ffff_ffff_ffffL) in vs', List.map (Lib.Fun.flip (@@) e.at) [ - Plain (Const (I32 i @@ e.at)); + Plain (Const (i @@ e.at)); Refer r; Plain (TableSet x); - Plain (Const (I32 (I32.add i 1l) @@ e.at)); + Plain (Const (addr_add i 1L @@ e.at)); Refer r; - Plain (Const (I32 (I32.sub n 1l) @@ e.at)); + Plain (Const (addr_sub n 1L @@ e.at)); Plain (TableFill x); ] - | TableCopy (x, y), Num (I32 n) :: Num (I32 s) :: Num (I32 d) :: vs' -> + | TableCopy (x, y), Num n :: Num s :: Num d :: vs' -> + let n_64 = addr_of_num n in + let s_64 = addr_of_num s in + let d_64 = addr_of_num d in if table_oob c.frame x d n || table_oob c.frame y s n then vs', [Trapping (table_error e.at Table.Bounds) @@ e.at] - else if n = 0l then + else if n_64 = 0L then vs', [] - else if I32.le_u d s then + else if I64.le_u d_64 s_64 then vs', List.map (Lib.Fun.flip (@@) e.at) [ - Plain (Const (I32 d @@ e.at)); - Plain (Const (I32 s @@ e.at)); + Plain (Const (d @@ e.at)); + Plain (Const (s @@ e.at)); Plain (TableGet y); Plain (TableSet x); - Plain (Const (I32 (I32.add d 1l) @@ e.at)); - Plain (Const (I32 (I32.add s 1l) @@ e.at)); - Plain (Const (I32 (I32.sub n 1l) @@ e.at)); + Plain (Const (addr_add d 1L @@ e.at)); + Plain (Const (addr_add s 1L @@ e.at)); + Plain (Const (addr_sub n 1L @@ e.at)); Plain (TableCopy (x, y)); ] else (* d > s *) - let n' = I32.sub n 1l in + let n' = I64.sub n_64 1L in vs', List.map (Lib.Fun.flip (@@) e.at) [ - Plain (Const (I32 (I32.add d n') @@ e.at)); - Plain (Const (I32 (I32.add s n') @@ e.at)); + Plain (Const (addr_add d n' @@ e.at)); + Plain (Const (addr_add s n' @@ e.at)); Plain (TableGet y); Plain (TableSet x); - Plain (Const (I32 d @@ e.at)); - Plain (Const (I32 s @@ e.at)); - Plain (Const (I32 n' @@ e.at)); + Plain (Const (d @@ e.at)); + Plain (Const (s @@ e.at)); + Plain (Const (addr_sub n 1L @@ e.at)); Plain (TableCopy (x, y)); ] - | TableInit (x, y), Num (I32 n) :: Num (I32 s) :: Num (I32 d) :: vs' -> + | TableInit (x, y), Num n :: Num s :: Num d :: vs' -> + let n_64 = addr_of_num n in + let s_64 = addr_of_num s in if table_oob c.frame x d n || elem_oob c.frame y s n then vs', [Trapping (table_error e.at Table.Bounds) @@ e.at] - else if n = 0l then + else if n_64 = 0L then vs', [] else let seg = elem c.frame.inst y in vs', List.map (Lib.Fun.flip (@@) e.at) [ - Plain (Const (I32 d @@ e.at)); - Refer (Elem.load seg s); + Plain (Const (d @@ e.at)); + Refer (Elem.load seg s_64); Plain (TableSet x); - Plain (Const (I32 (I32.add d 1l) @@ e.at)); - Plain (Const (I32 (I32.add s 1l) @@ e.at)); - Plain (Const (I32 (I32.sub n 1l) @@ e.at)); + Plain (Const (addr_add d 1L @@ e.at)); + Plain (Const (addr_add s 1L @@ e.at)); + Plain (Const (addr_sub n 1L @@ e.at)); Plain (TableInit (x, y)); ] @@ -438,164 +603,170 @@ let rec step (c : config) : config = Elem.drop seg; vs, [] - | Load (x, {offset; ty; pack; _}), Num (I32 i) :: vs' -> + | Load (x, {offset; ty; pack; _}), Num i :: vs' -> + let i_64 = addr_of_num i in let mem = memory c.frame.inst x in - let a = I64_convert.extend_i32_u i in (try let n = match pack with - | None -> Memory.load_num mem a offset ty - | Some (sz, ext) -> Memory.load_num_packed sz ext mem a offset ty + | None -> Memory.load_num mem i_64 offset ty + | Some (sz, ext) -> Memory.load_num_packed sz ext mem i_64 offset ty in Num n :: vs', [] with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at]) - | Store (x, {offset; pack; _}), Num n :: Num (I32 i) :: vs' -> + | Store (x, {offset; pack; _}), Num n :: Num i :: vs' -> + let i_64 = addr_of_num i in let mem = memory c.frame.inst x in - let a = I64_convert.extend_i32_u i in (try (match pack with - | None -> Memory.store_num mem a offset n - | Some sz -> Memory.store_num_packed sz mem a offset n + | None -> Memory.store_num mem i_64 offset n + | Some sz -> Memory.store_num_packed sz mem i_64 offset n ); vs', [] with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at]); - | VecLoad (x, {offset; ty; pack; _}), Num (I32 i) :: vs' -> + | VecLoad (x, {offset; ty; pack; _}), Num i :: vs' -> + let i_64 = addr_of_num i in let mem = memory c.frame.inst x in - let a = I64_convert.extend_i32_u i in (try let v = match pack with - | None -> Memory.load_vec mem a offset ty - | Some (sz, ext) -> Memory.load_vec_packed sz ext mem a offset ty + | None -> Memory.load_vec mem i_64 offset ty + | Some (sz, ext) -> Memory.load_vec_packed sz ext mem i_64 offset ty in Vec v :: vs', [] with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at]) - | VecStore (x, {offset; _}), Vec v :: Num (I32 i) :: vs' -> + | VecStore (x, {offset; _}), Vec v :: Num i :: vs' -> + let i_64 = addr_of_num i in let mem = memory c.frame.inst x in - let addr = I64_convert.extend_i32_u i in (try - Memory.store_vec mem addr offset v; + Memory.store_vec mem i_64 offset v; vs', [] with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at]); - | VecLoadLane (x, {offset; ty; pack; _}, j), Vec (V128 v) :: Num (I32 i) :: vs' -> + | VecLoadLane (x, {offset; ty; pack; _}, j), Vec (V128 v) :: Num i :: vs' -> + let i_64 = addr_of_num i in let mem = memory c.frame.inst x in - let addr = I64_convert.extend_i32_u i in (try let v = match pack with | Pack8 -> V128.I8x16.replace_lane j v - (I32Num.of_num 0 (Memory.load_num_packed Pack8 SX mem addr offset I32T)) + (I32Num.of_num 0 (Memory.load_num_packed Pack8 SX mem i_64 offset I32T)) | Pack16 -> V128.I16x8.replace_lane j v - (I32Num.of_num 0 (Memory.load_num_packed Pack16 SX mem addr offset I32T)) + (I32Num.of_num 0 (Memory.load_num_packed Pack16 SX mem i_64 offset I32T)) | Pack32 -> V128.I32x4.replace_lane j v - (I32Num.of_num 0 (Memory.load_num mem addr offset I32T)) + (I32Num.of_num 0 (Memory.load_num mem i_64 offset I32T)) | Pack64 -> V128.I64x2.replace_lane j v - (I64Num.of_num 0 (Memory.load_num mem addr offset I64T)) + (I64Num.of_num 0 (Memory.load_num mem i_64 offset I64T)) in Vec (V128 v) :: vs', [] with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at]) - | VecStoreLane (x, {offset; ty; pack; _}, j), Vec (V128 v) :: Num (I32 i) :: vs' -> + | VecStoreLane (x, {offset; ty; pack; _}, j), Vec (V128 v) :: Num i :: vs' -> + let i_64 = addr_of_num i in let mem = memory c.frame.inst x in - let addr = I64_convert.extend_i32_u i in (try (match pack with | Pack8 -> - Memory.store_num_packed Pack8 mem addr offset (I32 (V128.I8x16.extract_lane_s j v)) + Memory.store_num_packed Pack8 mem i_64 offset (I32 (V128.I8x16.extract_lane_s j v)) | Pack16 -> - Memory.store_num_packed Pack16 mem addr offset (I32 (V128.I16x8.extract_lane_s j v)) + Memory.store_num_packed Pack16 mem i_64 offset (I32 (V128.I16x8.extract_lane_s j v)) | Pack32 -> - Memory.store_num mem addr offset (I32 (V128.I32x4.extract_lane_s j v)) + Memory.store_num mem i_64 offset (I32 (V128.I32x4.extract_lane_s j v)) | Pack64 -> - Memory.store_num mem addr offset (I64 (V128.I64x2.extract_lane_s j v)) + Memory.store_num mem i_64 offset (I64 (V128.I64x2.extract_lane_s j v)) ); vs', [] with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at]) | MemorySize x, vs -> let mem = memory c.frame.inst x in - Num (I32 (Memory.size mem)) :: vs, [] + Num (num_of_addr (Memory.addr_type_of mem) (Memory.size mem)) :: vs, [] - | MemoryGrow x, Num (I32 delta) :: vs' -> + | MemoryGrow x, Num n :: vs' -> + let n_64 = addr_of_num n in let mem = memory c.frame.inst x in let old_size = Memory.size mem in let result = - try Memory.grow mem delta; old_size - with Memory.SizeOverflow | Memory.SizeLimit | Memory.OutOfMemory -> -1l - in Num (I32 result) :: vs', [] + try Memory.grow mem n_64; old_size + with Memory.SizeOverflow | Memory.SizeLimit | Memory.OutOfMemory -> -1L + in Num (num_of_addr (Memory.addr_type_of mem) result) :: vs', [] - | MemoryFill x, Num (I32 n) :: Num k :: Num (I32 i) :: vs' -> + | MemoryFill x, Num n :: Num k :: Num i :: vs' -> + let n_64 = addr_of_num n in if mem_oob c.frame x i n then vs', [Trapping (memory_error e.at Memory.Bounds) @@ e.at] - else if n = 0l then + else if n_64 = 0L then vs', [] else vs', List.map (Lib.Fun.flip (@@) e.at) [ - Plain (Const (I32 i @@ e.at)); + Plain (Const (i @@ e.at)); Plain (Const (k @@ e.at)); Plain (Store - (x, {ty = I32T; align = 0; offset = 0l; pack = Some Pack8})); - Plain (Const (I32 (I32.add i 1l) @@ e.at)); + (x, {ty = I32T; align = 0; offset = 0L; pack = Some Pack8})); + Plain (Const (addr_add i 1L @@ e.at)); Plain (Const (k @@ e.at)); - Plain (Const (I32 (I32.sub n 1l) @@ e.at)); + Plain (Const (addr_sub n 1L @@ e.at)); Plain (MemoryFill x); ] - | MemoryCopy (x, y), Num (I32 n) :: Num (I32 s) :: Num (I32 d) :: vs' -> + | MemoryCopy (x, y), Num n :: Num s :: Num d :: vs' -> + let n_64 = addr_of_num n in + let s_64 = addr_of_num s in + let d_64 = addr_of_num d in if mem_oob c.frame x d n || mem_oob c.frame y s n then vs', [Trapping (memory_error e.at Memory.Bounds) @@ e.at] - else if n = 0l then + else if n_64 = 0L then vs', [] - else if I32.le_u d s then + else if I64.le_u d_64 s_64 then vs', List.map (Lib.Fun.flip (@@) e.at) [ - Plain (Const (I32 d @@ e.at)); - Plain (Const (I32 s @@ e.at)); + Plain (Const (d @@ e.at)); + Plain (Const (s @@ e.at)); Plain (Load - (y, {ty = I32T; align = 0; offset = 0l; pack = Some (Pack8, ZX)})); + (y, {ty = I32T; align = 0; offset = 0L; pack = Some (Pack8, ZX)})); Plain (Store - (x, {ty = I32T; align = 0; offset = 0l; pack = Some Pack8})); - Plain (Const (I32 (I32.add d 1l) @@ e.at)); - Plain (Const (I32 (I32.add s 1l) @@ e.at)); - Plain (Const (I32 (I32.sub n 1l) @@ e.at)); + (x, {ty = I32T; align = 0; offset = 0L; pack = Some Pack8})); + Plain (Const (addr_add d 1L @@ e.at)); + Plain (Const (addr_add s 1L @@ e.at)); + Plain (Const (addr_sub n 1L @@ e.at)); Plain (MemoryCopy (x, y)); ] else (* d > s *) - let n' = I32.sub n 1l in + let n' = I64.sub n_64 1L in vs', List.map (Lib.Fun.flip (@@) e.at) [ - Plain (Const (I32 (I32.add d n') @@ e.at)); - Plain (Const (I32 (I32.add s n') @@ e.at)); + Plain (Const (addr_add d n' @@ e.at)); + Plain (Const (addr_add s n' @@ e.at)); Plain (Load - (y, {ty = I32T; align = 0; offset = 0l; pack = Some (Pack8, ZX)})); + (y, {ty = I32T; align = 0; offset = 0L; pack = Some (Pack8, ZX)})); Plain (Store - (x, {ty = I32T; align = 0; offset = 0l; pack = Some Pack8})); - Plain (Const (I32 d @@ e.at)); - Plain (Const (I32 s @@ e.at)); - Plain (Const (I32 n' @@ e.at)); + (x, {ty = I32T; align = 0; offset = 0L; pack = Some Pack8})); + Plain (Const (d @@ e.at)); + Plain (Const (s @@ e.at)); + Plain (Const (addr_sub n 1L @@ e.at)); Plain (MemoryCopy (x, y)); ] - | MemoryInit (x, y), Num (I32 n) :: Num (I32 s) :: Num (I32 d) :: vs' -> + | MemoryInit (x, y), Num n :: Num s :: Num d :: vs' -> + let n_64 = addr_of_num n in + let s_64 = addr_of_num s in if mem_oob c.frame x d n || data_oob c.frame y s n then vs', [Trapping (memory_error e.at Memory.Bounds) @@ e.at] - else if n = 0l then + else if n_64 = 0L then vs', [] else let seg = data c.frame.inst y in - let a = I64_convert.extend_i32_u s in - let b = Data.load_byte seg a in + let b = Data.load_byte seg s_64 in vs', List.map (Lib.Fun.flip (@@) e.at) [ - Plain (Const (I32 d @@ e.at)); + Plain (Const (d @@ e.at)); Plain (Const (I32 (I32.of_int_u (Char.code b)) @@ e.at)); Plain (Store - (x, {ty = I32T; align = 0; offset = 0l; pack = Some Pack8})); - Plain (Const (I32 (I32.add d 1l) @@ e.at)); - Plain (Const (I32 (I32.add s 1l) @@ e.at)); - Plain (Const (I32 (I32.sub n 1l) @@ e.at)); + (x, {ty = I64T; align = 0; offset = 0L; pack = Some Pack8})); + Plain (Const (addr_add d 1L @@ e.at)); + Plain (Const (addr_add s 1L @@ e.at)); + Plain (Const (addr_sub n 1L @@ e.at)); Plain (MemoryInit (x, y)); ] @@ -653,7 +824,7 @@ let rec step (c : config) : config = let args, vs'' = match initop with | Explicit -> - let args, vs'' = split (List.length fts) vs' e.at in + let args, vs'' = i32_split (Lib.List32.length fts) vs' e.at in List.rev args, vs'' | Implicit -> let ts = List.map unpacked_field_type fts in @@ -702,34 +873,39 @@ let rec step (c : config) : config = in Ref (Aggr.ArrayRef array) :: vs'', [] | ArrayNewFixed (x, n), vs' -> - let args, vs'' = split (I32.to_int_u n) vs' e.at in + let args, vs'' = i32_split n vs' e.at in let array = try Aggr.alloc_array (type_ c.frame.inst x) (List.rev args) with Failure _ -> Crash.error e.at "type mismatch packing value" in Ref (Aggr.ArrayRef array) :: vs'', [] - | ArrayNewElem (x, y), Num (I32 n) :: Num (I32 s) :: vs' -> + | ArrayNewElem (x, y), Num n :: Num s :: vs' -> + let n_64 = addr_of_num n in + let s_64 = addr_of_num s in if elem_oob c.frame y s n then vs', [Trapping (table_error e.at Table.Bounds) @@ e.at] else let seg = elem c.frame.inst y in - let rs = Lib.List32.init n (fun i -> Elem.load seg (Int32.add s i)) in + let rs = Lib.List64.init n_64 + (fun i -> Elem.load seg (Int64.add s_64 i)) in let args = List.map (fun r -> Ref r) rs in let array = try Aggr.alloc_array (type_ c.frame.inst x) args with Failure _ -> Crash.error e.at "type mismatch packing value" in Ref (Aggr.ArrayRef array) :: vs', [] - | ArrayNewData (x, y), Num (I32 n) :: Num (I32 s) :: vs' -> + | ArrayNewData (x, y), Num n :: Num s :: vs' -> + let n_64 = addr_of_num n in + let s_64 = addr_of_num s in if data_oob c.frame y s n then vs', [Trapping (memory_error e.at Memory.Bounds) @@ e.at] else let ArrayT (FieldT (_mut, st)) = array_type c.frame.inst x in let seg = data c.frame.inst y in - let args = Lib.List32.init n + let args = Lib.List64.init n_64 (fun i -> - let a = I32.(add s (mul i (I32.of_int_u (storage_size st)))) in - Data.load_val_storage seg (I64_convert.extend_i32_u a) st + let a = I64.(add s_64 (mul i (I64.of_int_u (storage_size st)))) in + Data.load_val_storage seg a st ) in let array = @@ -844,17 +1020,18 @@ let rec step (c : config) : config = vs', [Trapping "null array reference" @@ e.at] | ArrayInitData (x, y), - Num (I32 n) :: Num (I32 s) :: Num (I32 d) :: Ref (Aggr.ArrayRef a) :: vs' -> + Num (I32 n) :: Num s :: Num (I32 d) :: Ref (Aggr.ArrayRef a) :: vs' -> + let s_64 = addr_of_num s in if array_oob a d n then vs', [Trapping "out of bounds array access" @@ e.at] - else if data_oob c.frame y s n then + else if data_oob c.frame y s (I64 (I64_convert.extend_i32_u n)) then vs', [Trapping (memory_error e.at Memory.Bounds) @@ e.at] else if n = 0l then vs', [] else let ArrayT (FieldT (_mut, st)) = array_type c.frame.inst x in let seg = data c.frame.inst y in - let v = Data.load_val_storage seg (I64_convert.extend_i32_u s) st in + let v = Data.load_val_storage seg s_64 st in vs', List.map (Lib.Fun.flip (@@) e.at) [ Refer (Aggr.ArrayRef a); Plain (Const (I32 d @@ e.at)); @@ -862,7 +1039,7 @@ let rec step (c : config) : config = Plain (ArraySet x); Refer (Aggr.ArrayRef a); Plain (Const (I32 (I32.add d 1l) @@ e.at)); - Plain (Const (I32 (I32.add s (I32.of_int_u (storage_size st))) @@ e.at)); + Plain (Const (addr_add s (I64.of_int_u (storage_size st)) @@ e.at)); Plain (Const (I32 (I32.sub n 1l) @@ e.at)); Plain (ArrayInitData (x, y)); ] @@ -872,16 +1049,17 @@ let rec step (c : config) : config = vs', [Trapping "null array reference" @@ e.at] | ArrayInitElem (x, y), - Num (I32 n) :: Num (I32 s) :: Num (I32 d) :: Ref (Aggr.ArrayRef a) :: vs' -> + Num (I32 n) :: Num s :: Num (I32 d) :: Ref (Aggr.ArrayRef a) :: vs' -> + let s_64 = addr_of_num s in if array_oob a d n then vs', [Trapping "out of bounds array access" @@ e.at] - else if elem_oob c.frame y s n then + else if elem_oob c.frame y s (I64 (I64_convert.extend_i32_u n)) then vs', [Trapping (table_error e.at Table.Bounds) @@ e.at] else if n = 0l then vs', [] else let seg = elem c.frame.inst y in - let v = Ref (Elem.load seg s) in + let v = Ref (Elem.load seg s_64) in vs', List.map (Lib.Fun.flip (@@) e.at) [ Refer (Aggr.ArrayRef a); Plain (Const (I32 d @@ e.at)); @@ -889,7 +1067,7 @@ let rec step (c : config) : config = Plain (ArraySet x); Refer (Aggr.ArrayRef a); Plain (Const (I32 (I32.add d 1l) @@ e.at)); - Plain (Const (I32 (I32.add s 1l) @@ e.at)); + Plain (Const (addr_add s 1L @@ e.at)); Plain (Const (I32 (I32.sub n 1l) @@ e.at)); Plain (ArrayInitElem (x, y)); ] @@ -944,6 +1122,10 @@ let rec step (c : config) : config = (try Vec (Eval_vec.eval_binop binop n1 n2) :: vs', [] with exn -> vs', [Trapping (numeric_error e.at exn) @@ e.at]) + | VecTernary ternop, Vec v3 :: Vec v2 :: Vec v1 :: vs' -> + (try Vec (Eval_vec.eval_ternop ternop v1 v2 v3) :: vs', [] + with exn -> vs', [Trapping (numeric_error e.at exn) @@ e.at]) + | VecCompare relop, Vec n2 :: Vec n1 :: vs' -> (try Vec (Eval_vec.eval_relop relop n1 n2) :: vs', [] with exn -> vs', [Trapping (numeric_error e.at exn) @@ e.at]) @@ -1014,6 +1196,13 @@ let rec step (c : config) : config = | Label (n, es0, (vs', [])), vs -> vs' @ vs, [] + | Label (n, es0, (vs', {it = Suspending (tagt, vs1, contref, ctxt); at} :: es')), vs -> + let ctxt' code = [], [Label (n, es0, compose (ctxt code) (vs', es')) @@ e.at] in + vs, [Suspending (tagt, vs1, contref, ctxt') @@ at] + + | Label (n, es0, (vs', {it = ReturningInvoke (vs0, f); at} :: es')), vs -> + vs, [ReturningInvoke (vs0, f) @@ at] + | Label (n, es0, (vs', {it = Breaking (0l, vs0); at} :: es')), vs -> take n vs0 e.at @ vs, List.map plain es0 @@ -1030,6 +1219,16 @@ let rec step (c : config) : config = | Frame (n, frame', (vs', [])), vs -> vs' @ vs, [] + | Frame (n, frame', (vs', {it = Trapping msg; at} :: es')), vs -> + vs, [Trapping msg @@ at] + + | Frame (n, frame', (vs', {it = Throwing (a, vs0); at} :: es')), vs -> + vs, [Throwing (a, vs0) @@ at] + + | Frame (n, frame', (vs', {it = Suspending (tagt, vs1, contref, ctxt); at} :: es')), vs -> + let ctxt' code = [], [Frame (n, frame', compose (ctxt code) (vs', es')) @@ e.at] in + vs, [Suspending (tagt, vs1, contref, ctxt') @@ at] + | Frame (n, frame', (vs', {it = Returning vs0; at} :: es')), vs -> take n vs0 e.at @ vs, [] @@ -1055,7 +1254,7 @@ let rec step (c : config) : config = | Handler (n, {it = CatchRef (x1, x2); _} :: cs, (vs', {it = Throwing (a, vs0); at} :: es')), vs -> if a == tag c.frame.inst x1 then - Ref (ExnRef (a, vs0)) :: vs0 @ vs, [Plain (Br x2) @@ e.at] + Ref Exn.(ExnRef (Exn (a, vs0))) :: vs0 @ vs, [Plain (Br x2) @@ e.at] else vs, [Handler (n, cs, (vs', {it = Throwing (a, vs0); at} :: es')) @@ e.at] @@ -1063,11 +1262,15 @@ let rec step (c : config) : config = vs, [Plain (Br x) @@ e.at] | Handler (n, {it = CatchAllRef x; _} :: cs, (vs', {it = Throwing (a, vs0); at} :: es')), vs -> - Ref (ExnRef (a, vs0)) :: vs, [Plain (Br x) @@ e.at] + Ref Exn.(ExnRef (Exn (a, vs0))) :: vs, [Plain (Br x) @@ e.at] | Handler (n, [], (vs', {it = Throwing (a, vs0); at} :: es')), vs -> vs, [Throwing (a, vs0) @@ at] + | Handler (n, cs, (vs', {it = Suspending (tagt, vs1, contref, ctxt); at} :: es')), vs -> + let ctxt' code = [], [Handler (n, cs, compose (ctxt code) (vs', es')) @@ e.at] in + vs, [Suspending (tagt, vs1, contref, ctxt') @@ at] + | Handler (n, cs, (vs', e' :: es')), vs when is_jumping e' -> vs, [e'] @@ -1083,7 +1286,7 @@ let rec step (c : config) : config = let n1, n2 = List.length ts1, List.length ts2 in let args, vs' = split n1 vs e.at in (match f with - | Func.AstFunc (_, inst', func) -> + | Func.AstFunc (_, inst', func) -> let {locals; body; _} = func.it in let m = Lib.Promise.value inst' in let s = subst_of m in @@ -1097,6 +1300,38 @@ let rec step (c : config) : config = (try List.rev (f (List.rev args)) @ vs', [] with Crash (_, msg) -> Crash.error e.at msg) ) + + | Prompt (hso, (vs', [])), vs -> + vs' @ vs, [] + + | Prompt ((hs, _), (vs', {it = Suspending (tagt, vs1, None, ctxt); at} :: es')), vs + when List.mem_assq tagt hs -> + let FuncT (_, ts) = func_type_of_tag_type c.frame.inst (Tag.type_of tagt) in + let ctxt' code = compose (ctxt code) (vs', es') in + [Ref (ContRef (ref (Some (Lib.List32.length ts, ctxt'))))] @ vs1 @ vs, + [Plain (Br (List.assq tagt hs)) @@ e.at] + + | Prompt ((_, hs) as hso, (vs', {it = Suspending (tagt, vs1, Some (ar, ContRef ({contents = Some (_, ctxt)} as cont)), ctxt'); at} :: es')), vs + when List.memq tagt hs -> + let ctxt'' code = compose (ctxt' code) (vs', es') in + let cont' = Ref (ContRef (ref (Some (ar, ctxt'')))) in + let args = cont' :: vs1 in + cont := None; + vs' @ vs, [Prompt (hso, ctxt (args, [])) @@ e.at] + + | Prompt (hso, (vs', {it = Suspending (tagt, vs1, contref, ctxt); at} :: es')), vs -> + let ctxt' code = [], [Prompt (hso, compose (ctxt code) (vs', es')) @@ e.at] in + vs, [Suspending (tagt, vs1, contref, ctxt') @@ at] + + | Prompt (hso, (vs', e' :: es')), vs when is_jumping e' -> + vs, [e'] + + | Prompt (hso, code'), vs -> + let c' = step {c with code = code'} in + vs, [Prompt (hso, c'.code) @@ e.at] + + | Suspending (_, _, _, _), _ -> assert false + in {c with code = vs', es' @ List.tl es} @@ -1105,8 +1340,15 @@ let rec eval (c : config) : value stack = | vs, [] -> vs - | vs, {it = Trapping msg; at} :: _ -> - Trap.error at msg + | vs, e::_ when is_jumping e -> + (match e.it with + | Trapping msg -> Trap.error e.at msg + | Throwing _ -> Exception.error e.at "unhandled exception" + | Suspending _ -> Suspension.error e.at "unhandled tag" + | Returning _ | ReturningInvoke _ -> Crash.error e.at "undefined frame" + | Breaking _ -> Crash.error e.at "undefined label" + | _ -> assert false + ) | vs, {it = Throwing (a, args); at} :: _ -> let msg = "uncaught exception with args (" ^ string_of_values args ^ ")" in @@ -1139,7 +1381,6 @@ let eval_const (inst : module_inst) (const : const) : value = | [v] -> v | vs -> Crash.error const.at "wrong number of results on stack" - (* Modules *) let init_type (inst : module_inst) (type_ : type_) : module_inst = @@ -1176,10 +1417,6 @@ let init_func (inst : module_inst) (f : func) : module_inst = let func = Func.alloc (type_ inst f.it.ftype) (Lib.Promise.make ()) f in {inst with funcs = inst.funcs @ [func]} -let init_tag (inst : module_inst) (t : tag) : module_inst = - let tag = Tag.alloc (TagT (type_ inst t.it.tgtype)) in - {inst with tags = inst.tags @ [tag]} - let init_global (inst : module_inst) (glob : global) : module_inst = let {gtype; ginit} = glob.it in let gt = subst_global_type (subst_of inst) gtype in @@ -1209,6 +1446,10 @@ let init_elem (inst : module_inst) (seg : elem_segment) : module_inst = let elem = Elem.alloc (List.map (fun c -> as_ref (eval_const inst c)) einit) in {inst with elems = inst.elems @ [elem]} +let init_tag (inst : module_inst) (t : tag) : module_inst = + let tag = Tag.alloc (TagT (type_ inst t.it.tgtype)) in + {inst with tags = inst.tags @ [tag]} + let init_data (inst : module_inst) (seg : data_segment) : module_inst = let {dinit; _} = seg.it in let data = Data.alloc dinit in @@ -1226,7 +1467,6 @@ let init_export (inst : module_inst) (ex : export) : module_inst = in {inst with exports = inst.exports @ [(name, ext)]} - let init_func_inst (inst : module_inst) (func : func_inst) = match func with | Func.AstFunc (_, prom, _) when Lib.Promise.value_opt prom = None -> @@ -1281,6 +1521,7 @@ let init (m : module_) (exts : extern list) : module_inst = |> init_list2 init_import exts m.it.imports |> init_list init_func m.it.funcs |> init_list init_global m.it.globals + |> init_list init_tag m.it.tags |> init_list init_table m.it.tables |> init_list init_memory m.it.memories |> init_list init_tag m.it.tags diff --git a/interpreter/exec/eval.mli b/interpreter/exec/eval.mli index 196f8997e..089aaeca4 100644 --- a/interpreter/exec/eval.mli +++ b/interpreter/exec/eval.mli @@ -4,8 +4,9 @@ open Instance exception Link of Source.region * string exception Trap of Source.region * string exception Exception of Source.region * string -exception Crash of Source.region * string +exception Suspension of Source.region * string exception Exhaustion of Source.region * string +exception Crash of Source.region * string val init : Ast.module_ -> extern list -> module_inst (* raises Link, Trap *) val invoke : func_inst -> value list -> value list (* raises Trap *) diff --git a/interpreter/exec/eval_vec.ml b/interpreter/exec/eval_vec.ml index a3b9986ff..2296ed94f 100644 --- a/interpreter/exec/eval_vec.ml +++ b/interpreter/exec/eval_vec.ml @@ -61,6 +61,7 @@ struct | I8x16 MaxS -> V128.I8x16.max_s | I8x16 MaxU -> V128.I8x16.max_u | I8x16 AvgrU -> V128.I8x16.avgr_u + | I8x16 RelaxedSwizzle -> V128.V8x16.swizzle | I16x8 NarrowS -> V128.I16x8_convert.narrow_s | I16x8 NarrowU -> V128.I16x8_convert.narrow_u | I16x8 Add -> V128.I16x8.add @@ -80,6 +81,8 @@ struct | I16x8 ExtMulLowU -> V128.I16x8_convert.extmul_low_u | I16x8 ExtMulHighU -> V128.I16x8_convert.extmul_high_u | I16x8 Q15MulRSatS -> V128.I16x8.q15mulr_sat_s + | I16x8 RelaxedQ15MulRS -> V128.I16x8.q15mulr_sat_s + | I16x8 RelaxedDot -> V128.I16x8_convert.dot_s | I32x4 Add -> V128.I32x4.add | I32x4 Sub -> V128.I32x4.sub | I32x4 MinS -> V128.I32x4.min_s @@ -107,6 +110,8 @@ struct | F32x4 Max -> V128.F32x4.max | F32x4 Pmin -> V128.F32x4.pmin | F32x4 Pmax -> V128.F32x4.pmax + | F32x4 RelaxedMin -> V128.F32x4.min + | F32x4 RelaxedMax -> V128.F32x4.max | F64x2 Add -> V128.F64x2.add | F64x2 Sub -> V128.F64x2.sub | F64x2 Mul -> V128.F64x2.mul @@ -115,9 +120,25 @@ struct | F64x2 Max -> V128.F64x2.max | F64x2 Pmin -> V128.F64x2.pmin | F64x2 Pmax -> V128.F64x2.pmax + | F64x2 RelaxedMin -> V128.F64x2.min + | F64x2 RelaxedMax -> V128.F64x2.max | _ -> assert false in fun v1 v2 -> to_vec (f (of_vec 1 v1) (of_vec 2 v2)) + let ternop (op : ternop) = + let f = match op with + | F32x4 RelaxedMadd -> V128.F32x4.fma + | F32x4 RelaxedNmadd -> V128.F32x4.fnma + | F64x2 RelaxedMadd -> V128.F64x2.fma + | F64x2 RelaxedNmadd -> V128.F64x2.fnma + | I8x16 RelaxedLaneselect -> V128.V1x128.bitselect + | I16x8 RelaxedLaneselect -> V128.V1x128.bitselect + | I32x4 RelaxedLaneselect -> V128.V1x128.bitselect + | I64x2 RelaxedLaneselect -> V128.V1x128.bitselect + | I32x4 RelaxedDotAccum -> V128.I32x4_convert.dot_s_accum + | _ -> assert false + in fun v1 v2 v3 -> to_vec (f (of_vec 1 v1) (of_vec 2 v2) (of_vec 3 v3)) + let relop (op : relop) = let f = match op with | I8x16 Eq -> V128.I8x16.eq @@ -187,6 +208,10 @@ struct | I32x4 TruncSatUF32x4 -> V128.I32x4_convert.trunc_sat_f32x4_u | I32x4 TruncSatSZeroF64x2 -> V128.I32x4_convert.trunc_sat_f64x2_s_zero | I32x4 TruncSatUZeroF64x2 -> V128.I32x4_convert.trunc_sat_f64x2_u_zero + | I32x4 RelaxedTruncSF32x4 -> V128.I32x4_convert.trunc_sat_f32x4_s + | I32x4 RelaxedTruncUF32x4 -> V128.I32x4_convert.trunc_sat_f32x4_u + | I32x4 RelaxedTruncSZeroF64x2 -> V128.I32x4_convert.trunc_sat_f64x2_s_zero + | I32x4 RelaxedTruncUZeroF64x2 -> V128.I32x4_convert.trunc_sat_f64x2_u_zero | I32x4 ExtAddPairwiseS -> V128.I32x4_convert.extadd_pairwise_s | I32x4 ExtAddPairwiseU -> V128.I32x4_convert.extadd_pairwise_u | I64x2 ExtendLowS -> V128.I64x2_convert.extend_low_s @@ -301,6 +326,7 @@ let op v128 = function let eval_testop = op V128Op.testop let eval_unop = op V128Op.unop let eval_binop = op V128Op.binop +let eval_ternop = op V128Op.ternop let eval_relop = op V128Op.relop let eval_cvtop = op V128Op.cvtop let eval_shiftop = op V128Op.shiftop diff --git a/interpreter/exec/eval_vec.mli b/interpreter/exec/eval_vec.mli index 45b59033e..f4601bd01 100644 --- a/interpreter/exec/eval_vec.mli +++ b/interpreter/exec/eval_vec.mli @@ -3,6 +3,7 @@ open Value val eval_testop : Ast.vec_testop -> vec -> bool val eval_unop : Ast.vec_unop -> vec -> vec val eval_binop : Ast.vec_binop -> vec -> vec -> vec +val eval_ternop : Ast.vec_ternop -> vec -> vec -> vec -> vec val eval_relop : Ast.vec_relop -> vec -> vec -> vec val eval_cvtop : Ast.vec_cvtop -> vec -> vec val eval_shiftop : Ast.vec_shiftop -> vec -> num -> vec diff --git a/interpreter/exec/fxx.ml b/interpreter/exec/fxx.ml index 2385d194d..f94b387a2 100644 --- a/interpreter/exec/fxx.ml +++ b/interpreter/exec/fxx.ml @@ -43,6 +43,7 @@ sig val sub : t -> t -> t val mul : t -> t -> t val div : t -> t -> t + val fma : t -> t -> t -> t val sqrt : t -> t val min : t -> t -> t val max : t -> t -> t @@ -138,6 +139,13 @@ struct let mul x y = binary x ( *.) y let div x y = binary x (/.) y + let fma x y z = + let xf = to_float x in + let yf = to_float y in + let zf = to_float z in + let t = Float.fma xf yf zf in + if t = t then of_float t else determine_binary_nan x y + let sqrt x = unary Stdlib.sqrt x let ceil x = unary Stdlib.ceil x diff --git a/interpreter/exec/v128.ml b/interpreter/exec/v128.ml index c508f6b94..a4c4e85a8 100644 --- a/interpreter/exec/v128.ml +++ b/interpreter/exec/v128.ml @@ -191,6 +191,8 @@ sig val sub : t -> t -> t val mul : t -> t -> t val div : t -> t -> t + val fma : t -> t -> t -> t + val fnma : t -> t -> t -> t val min : t -> t -> t val max : t -> t -> t val pmin : t -> t -> t @@ -233,6 +235,9 @@ struct let sub = binop FXX.sub let mul = binop FXX.mul let div = binop FXX.div + let fma x y z = + of_lanes (Lib.List.map3 FXX.fma (to_lanes x) (to_lanes y) (to_lanes z)) + let fnma x y z = fma (unop FXX.neg x) y z let min = binop FXX.min let max = binop FXX.max let pmin = binop (fun x y -> if FXX.lt y x then y else x) @@ -375,9 +380,20 @@ struct let extadd ext x y = Int32.add (ext x) (ext y) let extadd_pairwise_s x = - I16x8.of_lanes (Lib.List.pairwise (extadd ext_s) (I8x16.to_lanes x)) + I16x8.of_lanes (Lib.List.map_pairwise (extadd ext_s) (I8x16.to_lanes x)) let extadd_pairwise_u x = - I16x8.of_lanes (Lib.List.pairwise (extadd ext_u) (I8x16.to_lanes x)) + I16x8.of_lanes (Lib.List.map_pairwise (extadd ext_u) (I8x16.to_lanes x)) + + let dot_s x y = + let xs = I8x16.to_lanes x in + let ys = I8x16.to_lanes y in + let rec dot xs ys = + match xs, ys with + | x1::x2::xs', y1::y2::ys' -> + Int32.(add (mul x1 y1) (mul x2 y2)) :: dot xs' ys' + | [], [] -> [] + | _, _ -> assert false + in I16x8.of_lanes (dot xs ys) end module I32x4_convert = @@ -412,6 +428,20 @@ struct | _, _ -> assert false in I32x4.of_lanes (dot xs ys) + let dot_s_accum x y z = + let xs = I8x16.to_lanes x in + let ys = I8x16.to_lanes y in + let rec dot xs ys = + match xs, ys with + | x1::x2::x3::x4::xs', y1::y2::y3::y4::ys' -> + Int32.(add + (add (mul x1 y1) (mul x2 y2)) + (add (mul x3 y3) (mul x4 y4))) + :: dot xs' ys' + | [], [] -> [] + | _, _ -> assert false + in I32x4.add (I32x4.of_lanes (dot xs ys)) z + let extmul_low_s x y = I32x4.mul (extend_low_s x) (extend_low_s y) let extmul_high_s x y = I32x4.mul (extend_high_s x) (extend_high_s y) let extmul_low_u x y = I32x4.mul (extend_low_u x) (extend_low_u y) @@ -419,9 +449,9 @@ struct let extadd ext x y = Int32.add (ext x) (ext y) let extadd_pairwise_s x = - I32x4.of_lanes (Lib.List.pairwise (extadd ext_s) (I16x8.to_lanes x)) + I32x4.of_lanes (Lib.List.map_pairwise (extadd ext_s) (I16x8.to_lanes x)) let extadd_pairwise_u x = - I32x4.of_lanes (Lib.List.pairwise (extadd ext_u) (I16x8.to_lanes x)) + I32x4.of_lanes (Lib.List.map_pairwise (extadd ext_u) (I16x8.to_lanes x)) end module I64x2_convert = diff --git a/interpreter/exec/v128.mli b/interpreter/exec/v128.mli index f9535c158..e29477945 100644 --- a/interpreter/exec/v128.mli +++ b/interpreter/exec/v128.mli @@ -108,6 +108,8 @@ sig val sub : t -> t -> t val mul : t -> t -> t val div : t -> t -> t + val fma : t -> t -> t -> t + val fnma : t -> t -> t -> t val min : t -> t -> t val max : t -> t -> t val pmin : t -> t -> t @@ -163,6 +165,7 @@ sig val extmul_high_u : t -> t -> t val extadd_pairwise_s : t -> t val extadd_pairwise_u : t -> t + val dot_s : t -> t -> t end module I32x4_convert : @@ -176,6 +179,7 @@ sig val extend_low_u : t -> t val extend_high_u : t -> t val dot_s : t -> t -> t + val dot_s_accum : t -> t -> t -> t val extmul_low_s : t -> t -> t val extmul_high_s : t -> t -> t val extmul_low_u : t -> t -> t diff --git a/interpreter/host/spectest.ml b/interpreter/host/spectest.ml index a9a0f8446..8d6c039bf 100644 --- a/interpreter/host/spectest.ml +++ b/interpreter/host/spectest.ml @@ -20,11 +20,15 @@ let global (GlobalT (_, t) as gt) = in ExternGlobal (Global.alloc gt v) let table = - let tt = TableT ({min = 10l; max = Some 20l}, (Null, FuncHT)) in + let tt = TableT (I32AT, {min = 10L; max = Some 20L}, (Null, FuncHT)) in + ExternTable (Table.alloc tt (NullRef FuncHT)) + +let table64 = + let tt = TableT (I64AT, {min = 10L; max = Some 20L}, (Null, FuncHT)) in ExternTable (Table.alloc tt (NullRef FuncHT)) let memory = - let mt = MemoryT {min = 1l; max = Some 2l} in + let mt = MemoryT (I32AT, {min = 1L; max = Some 2L}) in ExternMemory (Memory.alloc mt) let func f ft = @@ -55,5 +59,6 @@ let lookup name t = | "global_f32", _ -> global (GlobalT (Cons, NumT F32T)) | "global_f64", _ -> global (GlobalT (Cons, NumT F64T)) | "table", _ -> table + | "table64", _ -> table64 | "memory", _ -> memory | _ -> raise Not_found diff --git a/interpreter/jslib/wast.ml b/interpreter/jslib/wast.ml index bc7edef07..266eab100 100644 --- a/interpreter/jslib/wast.ml +++ b/interpreter/jslib/wast.ml @@ -12,8 +12,8 @@ let () = let _, def = Parse.Module.parse_string (Js.to_string s) in let bs = match def.Source.it with - | Script.Textual m -> Encode.encode m - | Script.Encoded (_, bs) -> bs + | Script.Textual (m, cs) -> Encode.encode_with_custom (m, cs) + | Script.Encoded (_, bs) -> bs.Source.it | Script.Quoted (_, _) -> failwith "Unsupported" in let buf = new%js Typed_array.arrayBuffer (String.length bs) in let u8arr = new%js Typed_array.uint8Array_fromBuffer buf in diff --git a/interpreter/main/flags.ml b/interpreter/main/flags.ml index b92378aa2..14140ff35 100644 --- a/interpreter/main/flags.ml +++ b/interpreter/main/flags.ml @@ -5,4 +5,5 @@ let print_sig = ref false let dry = ref false let width = ref 80 let harness = ref true +let custom_reject = ref false let budget = ref 256 diff --git a/interpreter/main/main.ml b/interpreter/main/main.ml index 2377a6f9a..fc8a43f05 100644 --- a/interpreter/main/main.ml +++ b/interpreter/main/main.ml @@ -1,9 +1,16 @@ let name = "wasm" let version = "3.0.0" -let configure () = +let all_handlers = [ + (module Handler_custom : Custom.Handler); + (module Handler_name : Custom.Handler); + (module Handler_branch_hint : Custom.Handler); +] + +let configure custom_handlers = Import.register (Utf8.decode "spectest") Spectest.lookup; - Import.register (Utf8.decode "env") Env.lookup + Import.register (Utf8.decode "env") Env.lookup; + List.iter Custom.register custom_handlers let banner () = print_endline (name ^ " " ^ version ^ " reference interpreter") @@ -13,6 +20,15 @@ let usage = "Usage: " ^ name ^ " [option] [file ...]" let args = ref [] let add_arg source = args := !args @ [source] +let customs = ref [] +let add_custom name = + let n = Utf8.decode name in + match List.find_opt (fun (module H : Custom.Handler) -> n = H.name) all_handlers with + | Some h -> customs := !customs @ [h] + | None -> + prerr_endline ("option -c: unknown custom section \"" ^ name ^ "\""); + exit 1 + let quote s = "\"" ^ String.escaped s ^ "\"" let argspec = Arg.align @@ -28,6 +44,12 @@ let argspec = Arg.align " configure call depth budget (default is " ^ string_of_int !Flags.budget ^ ")"; "-w", Arg.Int (fun n -> Flags.width := n), " configure output width (default is " ^ string_of_int !Flags.width ^ ")"; + "-c", Arg.String add_custom, + " recognize custom section"; + "-ca", Arg.Unit (fun () -> customs := all_handlers), + " recognize all known custom section"; + "-cr", Arg.Set Flags.custom_reject, + " reject unrecognized custom sections"; "-s", Arg.Set Flags.print_sig, " show module signatures"; "-u", Arg.Set Flags.unchecked, " unchecked, do not perform validation"; "-j", Arg.Clear Flags.harness, " exclude harness for JS conversion"; @@ -39,9 +61,9 @@ let argspec = Arg.align let () = Printexc.record_backtrace true; try - configure (); Arg.parse argspec (fun file -> add_arg ("(input " ^ quote file ^ ")")) usage; + configure !customs; List.iter (fun arg -> if not (Run.run_string arg) then exit 1) !args; if !args = [] then Flags.interactive := true; if !Flags.interactive then begin diff --git a/interpreter/runtime/data.ml b/interpreter/runtime/data.ml index 785dcc608..66c2ad58d 100644 --- a/interpreter/runtime/data.ml +++ b/interpreter/runtime/data.ml @@ -1,6 +1,5 @@ type data = string ref type t = data -type address = Memory.address exception Bounds diff --git a/interpreter/runtime/data.mli b/interpreter/runtime/data.mli index f0074e366..86741d5c0 100644 --- a/interpreter/runtime/data.mli +++ b/interpreter/runtime/data.mli @@ -1,6 +1,7 @@ +open Value + type data type t = data -type address = Memory.address exception Bounds diff --git a/interpreter/runtime/elem.ml b/interpreter/runtime/elem.ml index 7a26055c9..101474f97 100644 --- a/interpreter/runtime/elem.ml +++ b/interpreter/runtime/elem.ml @@ -4,10 +4,10 @@ type t = elem exception Bounds let alloc rs = ref rs -let size seg = Lib.List32.length !seg +let size seg = Lib.List64.length !seg let load seg i = - if i < 0l || i >= Lib.List32.length !seg then raise Bounds; - Lib.List32.nth !seg i + if i < 0L || i >= Lib.List64.length !seg then raise Bounds; + Lib.List64.nth !seg i let drop seg = seg := [] diff --git a/interpreter/runtime/elem.mli b/interpreter/runtime/elem.mli index b9b35a11d..d61442907 100644 --- a/interpreter/runtime/elem.mli +++ b/interpreter/runtime/elem.mli @@ -7,5 +7,5 @@ exception Bounds val alloc : ref_ list -> elem val size : elem -> Table.size -val load : elem -> Table.index -> ref_ (* raises Bounds *) +val load : elem -> address -> ref_ (* raises Bounds *) val drop : elem -> unit diff --git a/interpreter/runtime/exn.ml b/interpreter/runtime/exn.ml new file mode 100644 index 000000000..d4009372e --- /dev/null +++ b/interpreter/runtime/exn.ml @@ -0,0 +1,38 @@ +open Types +open Value + +type exn_ = Exn of Tag.t * value list + +type ref_ += ExnRef of exn_ + +let alloc_exn tag vs = + let TagT dt = Tag.type_of tag in + assert Free.((def_type dt).types = Set.empty); + let FuncT (ts1, ts2) = as_func_str_type (expand_def_type dt) in + assert (List.length vs = List.length ts1); + assert (ts2 = []); + Exn (tag, vs) + +let type_of (Exn (tag, _)) = + let TagT dt = Tag.type_of tag in + dt + +let () = + let eq_ref' = !Value.eq_ref' in + Value.eq_ref' := fun r1 r2 -> + match r1, r2 with + | ExnRef _, ExnRef _ -> failwith "eq_ref" + | _, _ -> eq_ref' r1 r2 + +let () = + let type_of_ref' = !Value.type_of_ref' in + Value.type_of_ref' := function + | ExnRef e -> DefHT (type_of e) + | r -> type_of_ref' r + +let () = + let string_of_ref' = !Value.string_of_ref' in + Value.string_of_ref' := function + | ExnRef (Exn (_tag, vs)) -> + "(tag " ^ String.concat " " (List.map string_of_value vs) ^ ")" + | r -> string_of_ref' r diff --git a/interpreter/runtime/exn.mli b/interpreter/runtime/exn.mli new file mode 100644 index 000000000..13b034026 --- /dev/null +++ b/interpreter/runtime/exn.mli @@ -0,0 +1,10 @@ +open Types +open Value + +type exn_ = Exn of Tag.t * value list + +type ref_ += ExnRef of exn_ + +val alloc_exn : Tag.t -> value list -> exn_ + +val type_of : exn_ -> def_type diff --git a/interpreter/runtime/global.ml b/interpreter/runtime/global.ml index 3640cf404..cf69d2ac1 100644 --- a/interpreter/runtime/global.ml +++ b/interpreter/runtime/global.ml @@ -21,5 +21,4 @@ let load glob = let store glob v = let GlobalT (mut, t) = glob.ty in if mut <> Var then raise NotMutable; - if not (Match.match_val_type [] (type_of_value v) t) then raise Type; glob.content <- v diff --git a/interpreter/runtime/instance.ml b/interpreter/runtime/instance.ml index cdfa0074d..4b211c939 100644 --- a/interpreter/runtime/instance.ml +++ b/interpreter/runtime/instance.ml @@ -54,13 +54,6 @@ let () = | FuncRef _ -> "func" | r -> string_of_ref' r -let () = - let eq_ref' = !Value.eq_ref' in - Value.eq_ref' := fun r1 r2 -> - match r1, r2 with - | FuncRef f1, FuncRef f2 -> f1 == f2 - | _, _ -> eq_ref' r1 r2 - (* Projections *) diff --git a/interpreter/runtime/memory.ml b/interpreter/runtime/memory.ml index cf909691d..3c3742d94 100644 --- a/interpreter/runtime/memory.ml +++ b/interpreter/runtime/memory.ml @@ -1,10 +1,10 @@ open Types +open Value open Bigarray open Lib.Bigarray -type size = int32 (* number of pages *) -type address = int64 -type offset = int32 +type size = int64 (* number of pages *) +type offset = address type count = int32 type memory' = (int, int8_unsigned_elt, c_layout) Array1.t @@ -22,19 +22,24 @@ let page_size = 0x10000L (* 64 KiB *) let valid_limits {min; max} = match max with | None -> true - | Some m -> I32.le_u min m + | Some m -> I64.le_u min m + +let valid_size at i = + match at with + | I32AT -> I64.le_u i 0xffffL + | I64AT -> true let create n = - if I32.gt_u n 0x10000l then raise SizeOverflow else try - let size = Int64.(mul (of_int32 n) page_size) in + let size = Int64.(mul n page_size) in let mem = Array1_64.create Int8_unsigned C_layout size in Array1.fill mem 0; mem with Out_of_memory -> raise OutOfMemory -let alloc (MemoryT lim as ty) = +let alloc (MemoryT (at, lim) as ty) = assert Free.((memory_type ty).types = Set.empty); + if not (valid_size at lim.min) then raise SizeOverflow; if not (valid_limits lim) then raise Type; {ty; content = create lim.min} @@ -42,23 +47,27 @@ let bound mem = Array1_64.dim mem.content let size mem = - Int64.(to_int32 (div (bound mem) page_size)) + Int64.(div (bound mem) page_size) let type_of mem = mem.ty +let addr_type_of mem = + let MemoryT (at, _) = type_of mem in at + let grow mem delta = - let MemoryT lim = mem.ty in + let MemoryT (at, lim) = mem.ty in assert (lim.min = size mem); let old_size = lim.min in - let new_size = Int32.add old_size delta in - if I32.gt_u old_size new_size then raise SizeOverflow else + let new_size = Int64.add old_size delta in + if I64.gt_u old_size new_size then raise SizeOverflow else let lim' = {lim with min = new_size} in + if not (valid_size at new_size) then raise SizeOverflow else if not (valid_limits lim') then raise SizeLimit else let after = create new_size in let dim = Array1_64.dim mem.content in Array1.blit (Array1_64.sub mem.content 0L dim) (Array1_64.sub after 0L dim); - mem.ty <- MemoryT lim'; + mem.ty <- MemoryT (at, lim'); mem.content <- after let load_byte mem a = @@ -77,6 +86,7 @@ let load_bytes mem a n = Buffer.contents buf let store_bytes mem a bs = + if a < 0L then raise Bounds; for i = String.length bs - 1 downto 0 do store_byte mem Int64.(add a (of_int i)) (Char.code bs.[i]) done @@ -85,7 +95,7 @@ let store_bytes mem a bs = (* Typed accessors *) let effective_address a o = - let ea = Int64.(add a (of_int32 o)) in + let ea = Int64.(add a o) in if I64.lt_u ea a then raise Bounds; ea diff --git a/interpreter/runtime/memory.mli b/interpreter/runtime/memory.mli index 810f229c5..9f0edc7cd 100644 --- a/interpreter/runtime/memory.mli +++ b/interpreter/runtime/memory.mli @@ -1,11 +1,11 @@ open Types +open Value type memory type t = memory -type size = int32 (* number of pages *) -type address = int64 -type offset = int32 +type size = int64 (* number of pages *) +type offset = address type count = int32 exception Type @@ -18,6 +18,7 @@ val page_size : int64 val alloc : memory_type -> memory (* raises Type, SizeOverflow, OutOfMemory *) val type_of : memory -> memory_type +val addr_type_of : memory -> addr_type val size : memory -> size val bound : memory -> address val grow : memory -> size -> unit @@ -31,8 +32,6 @@ val store_bytes : memory -> address -> string -> unit (* raises Bounds *) (* Typed accessors *) -open Value - val load_num : memory -> address -> offset -> num_type -> num (* raises Bounds *) val store_num : diff --git a/interpreter/runtime/table.ml b/interpreter/runtime/table.ml index 7284af0cc..94a007ecf 100644 --- a/interpreter/runtime/table.ml +++ b/interpreter/runtime/table.ml @@ -1,9 +1,8 @@ open Types open Value -type size = int32 -type index = int32 -type count = int32 +type size = address +type offset = address type table = {mutable ty : table_type; mutable content : ref_ array} type t = table @@ -17,48 +16,63 @@ exception OutOfMemory let valid_limits {min; max} = match max with | None -> true - | Some m -> I32.le_u min m + | Some m -> I64.le_u min m + +let valid_size at i = + match at with + | I32AT -> I64.le_u i 0xffff_ffffL + | I64AT -> true let create size r = - try Lib.Array32.make size r + try Lib.Array64.make size r with Out_of_memory | Invalid_argument _ -> raise OutOfMemory -let alloc (TableT (lim, t) as ty) r = +let alloc (TableT (at, lim, t) as ty) r = assert Free.((ref_type t).types = Set.empty); + if not (valid_size at lim.min) then raise SizeOverflow; if not (valid_limits lim) then raise Type; {ty; content = create lim.min r} let size tab = - Lib.Array32.length tab.content + Lib.Array64.length tab.content let type_of tab = tab.ty +let addr_type_of tab = + let TableT (at, _, _) = type_of tab in at + +let addr_of_num x = + match x with + | I64 i -> i + | I32 i -> I64_convert.extend_i32_u i + | _ -> raise Type + let grow tab delta r = - let TableT (lim, t) = tab.ty in + let TableT (at, lim, t) = tab.ty in assert (lim.min = size tab); let old_size = lim.min in - let new_size = Int32.add old_size delta in - if I32.gt_u old_size new_size then raise SizeOverflow else + let new_size = Int64.add old_size delta in + if I64.gt_u old_size new_size then raise SizeOverflow else let lim' = {lim with min = new_size} in + if not (valid_size at new_size) then raise SizeOverflow else if not (valid_limits lim') then raise SizeLimit else let after = create new_size r in Array.blit tab.content 0 after 0 (Array.length tab.content); - tab.ty <- TableT (lim', t); + tab.ty <- TableT (at, lim', t); tab.content <- after let load tab i = - if i < 0l || i >= Lib.Array32.length tab.content then raise Bounds; - Lib.Array32.get tab.content i + if i < 0L || i >= Lib.Array64.length tab.content then raise Bounds; + Lib.Array64.get tab.content i let store tab i r = - let TableT (lim, t) = tab.ty in - if not (Match.match_ref_type [] (type_of_ref r) t) then raise Type; - if i < 0l || i >= Lib.Array32.length tab.content then raise Bounds; - Lib.Array32.set tab.content i r + let TableT (_at, _lim, t) = tab.ty in + if i < 0L || i >= Lib.Array64.length tab.content then raise Bounds; + Lib.Array64.set tab.content i r let blit tab offset rs = let data = Array.of_list rs in - let len = Lib.Array32.length data in - if offset < 0l || offset > Int32.sub (Lib.Array32.length tab.content) len then raise Bounds; - Lib.Array32.blit data 0l tab.content offset len + let len = Lib.Array64.length data in + if offset < 0L || offset > Int64.sub (Lib.Array64.length tab.content) len then raise Bounds; + Lib.Array64.blit data 0L tab.content offset len diff --git a/interpreter/runtime/table.mli b/interpreter/runtime/table.mli index 2d95b7411..53caf5965 100644 --- a/interpreter/runtime/table.mli +++ b/interpreter/runtime/table.mli @@ -4,9 +4,8 @@ open Value type table type t = table -type size = int32 -type index = int32 -type count = int32 +type size = address +type offset = address exception Type exception Bounds @@ -16,10 +15,12 @@ exception OutOfMemory val alloc : table_type -> ref_ -> table (* raises Type, OutOfMemory *) val type_of : table -> table_type +val addr_type_of : table -> addr_type val size : table -> size +val addr_of_num : num -> address val grow : table -> size -> ref_ -> unit (* raises SizeOverflow, SizeLimit, OutOfMemory *) -val load : table -> index -> ref_ (* raises Bounds *) -val store : table -> index -> ref_ -> unit (* raises Type, Bounds *) -val blit : table -> index -> ref_ list -> unit (* raises Bounds *) +val load : table -> address -> ref_ (* raises Bounds *) +val store : table -> address -> ref_ -> unit (* raises Type, Bounds *) +val blit : table -> address -> ref_ list -> unit (* raises Bounds *) diff --git a/interpreter/runtime/tag.ml b/interpreter/runtime/tag.ml index 3e0b9e499..cd6c93079 100644 --- a/interpreter/runtime/tag.ml +++ b/interpreter/runtime/tag.ml @@ -4,7 +4,7 @@ type tag = {ty : tag_type} type t = tag let alloc ty = - {ty} + {ty} -let type_of tg = - tg.ty +let type_of tag = + tag.ty diff --git a/interpreter/runtime/tag.mli b/interpreter/runtime/tag.mli index f8636674b..c4730e299 100644 --- a/interpreter/runtime/tag.mli +++ b/interpreter/runtime/tag.mli @@ -1,6 +1,6 @@ open Types -type tag = {ty : tag_type} +type tag type t = tag val alloc : tag_type -> tag diff --git a/interpreter/runtime/value.ml b/interpreter/runtime/value.ml index a3762725e..75c5cd2c9 100644 --- a/interpreter/runtime/value.ml +++ b/interpreter/runtime/value.ml @@ -18,7 +18,8 @@ type value = Num of num | Vec of vec | Ref of ref_ type t = value type ref_ += NullRef of heap_type -type ref_ += ExnRef of Tag.t * value list + +type address = I64.t (* Injection & projection *) @@ -95,27 +96,20 @@ let is_null_ref = function (* Typing *) let type_of_op = function - | I32 _ -> Types.I32T - | I64 _ -> Types.I64T - | F32 _ -> Types.F32T - | F64 _ -> Types.F64T - -let type_of_vecop = function - | V128 _ -> Types.V128T - -let type_of_num = function | I32 _ -> I32T | I64 _ -> I64T | F32 _ -> F32T | F64 _ -> F64T -let type_of_vec = function +let type_of_vecop = function | V128 _ -> V128T +let type_of_num = type_of_op +let type_of_vec = type_of_vecop + let type_of_ref' = ref (function _ -> assert false) let type_of_ref = function | NullRef t -> (Null, Match.bot_of_heap_type [] t) - | ExnRef _ -> (NoNull, ExnHT) | r -> (NoNull, !type_of_ref' r) let type_of_value = function @@ -289,6 +283,23 @@ let storage_bits_of_val st v = let value_of_bool b = Num (I32 (if b then 1l else 0l)) +let num_of_addr at i = + match at with + | I64AT -> I64 i + | I32AT -> I32 (I32_convert.wrap_i64 i) + +let addr_of_num x = + match x with + | I32 i -> I64_convert.extend_i32_u i + | I64 i -> i + | _ -> raise Type + +let addr_add n i = + num_of_addr (addr_type_of_num_type (type_of_num n)) (I64.add (addr_of_num n) i) +let addr_sub n i = + num_of_addr (addr_type_of_num_type (type_of_num n)) (I64.sub (addr_of_num n) i) + + let string_of_num = function | I32 i -> I32.to_string_s i | I64 i -> I64.to_string_s i @@ -310,7 +321,6 @@ let hex_string_of_vec = function let string_of_ref' = ref (function _ -> "ref") let string_of_ref = function | NullRef _ -> "null" - | ExnRef _ -> "exn" | r -> !string_of_ref' r let string_of_value = function diff --git a/interpreter/script/js.ml b/interpreter/script/js.ml index bd6a19e86..a125e9a0b 100644 --- a/interpreter/script/js.ml +++ b/interpreter/script/js.ml @@ -36,7 +36,10 @@ let spectest = { global_f32: 666.6, global_f64: 666.6, table: new WebAssembly.Table({initial: 10, maximum: 20, element: 'anyfunc'}), - memory: new WebAssembly.Memory({initial: 1, maximum: 2}) + table64: new WebAssembly.Table( + {initial: 10n, maximum: 20n, element: 'anyfunc', address: 'i64'}), + memory: new WebAssembly.Memory({initial: 1, maximum: 2}), + memory64: new WebAssembly.Memory({initial: 1n, maximum: 2n, address: 'i64'}) }; let handler = { @@ -68,8 +71,8 @@ function module(bytes, valid = true) { return new WebAssembly.Module(buffer); } -function instance(bytes, imports = registry) { - return new WebAssembly.Instance(module(bytes), imports); +function instance(mod, imports = registry) { + return new WebAssembly.Instance(mod, imports); } function call(instance, name, args) { @@ -96,6 +99,10 @@ function assert_malformed(bytes) { throw new Error("Wasm decoding failure expected"); } +function assert_malformed_custom(bytes) { + return; +} + function assert_invalid(bytes) { try { module(bytes, false) } catch (e) { if (e instanceof WebAssembly.CompileError) return; @@ -103,16 +110,18 @@ function assert_invalid(bytes) { throw new Error("Wasm validation failure expected"); } -function assert_unlinkable(bytes) { - let mod = module(bytes); +function assert_invalid_custom(bytes) { + return; +} + +function assert_unlinkable(mod) { try { new WebAssembly.Instance(mod, registry) } catch (e) { if (e instanceof WebAssembly.LinkError) return; } throw new Error("Wasm linking failure expected"); } -function assert_uninstantiable(bytes) { - let mod = module(bytes); +function assert_uninstantiable(mod) { try { new WebAssembly.Instance(mod, registry) } catch (e) { if (e instanceof WebAssembly.RuntimeError) return; } @@ -131,6 +140,14 @@ function assert_exception(action) { throw new Error("exception expected"); } +function assert_suspension(action) { + try { action() } catch (e) { + /* TODO: Not clear how to observe form JS */ + return; + } + throw new Error("Wasm exception expected"); +} + let StackOverflow; try { (function f() { 1 + f() })() } catch (e) { StackOverflow = e.constructor } @@ -159,7 +176,7 @@ function assert_return(action, ...expected) { // Note that JS can't reliably distinguish different NaN values, // so there's no good way to test that it's a canonical NaN. if (!Number.isNaN(actual[i])) { - throw new Error("Wasm return value NaN expected, got " + actual[i]); + throw new Error("Wasm NaN return value expected, got " + actual[i]); }; return; case "ref.i31": @@ -174,7 +191,7 @@ function assert_return(action, ...expected) { // For now, JS can't distinguish exported Wasm GC values, // so we only test for object. if (typeof actual[i] !== "object") { - throw new Error("Wasm function return value expected, got " + actual[i]); + throw new Error("Wasm object return value expected, got " + actual[i]); }; return; case "ref.func": @@ -208,35 +225,63 @@ module NameMap = Map.Make(struct type t = Ast.name let compare = compare end) module Map = Map.Make(String) type exports = extern_type NameMap.t -type modules = {mutable env : exports Map.t; mutable current : int} +type env = + { mutable mods : exports Map.t; + mutable insts : exports Map.t; + mutable current_mod : int; + mutable current_inst : int; + } let exports m : exports = let ModuleT (_, ets) = module_type_of m in List.fold_left (fun map (ExportT (et, name)) -> NameMap.add name et map) NameMap.empty ets -let modules () : modules = {env = Map.empty; current = 0} +let env () : env = + { mods = Map.empty; + insts = Map.empty; + current_mod = 0; + current_inst = 0; + } + +let current_mod (env : env) = "$$" ^ string_of_int env.current_mod +let of_mod_opt (env : env) = function + | None -> current_mod env + | Some x -> "$" ^ x.it -let current_var (mods : modules) = "$" ^ string_of_int mods.current -let of_var_opt (mods : modules) = function - | None -> current_var mods +let current_inst (env : env) = "$" ^ string_of_int env.current_inst +let of_inst_opt (env : env) = function + | None -> current_inst env | Some x -> x.it -let bind (mods : modules) x_opt m = +let bind_mod (env : env) x_opt m = let exports = exports m in - mods.current <- mods.current + 1; - mods.env <- Map.add (of_var_opt mods x_opt) exports mods.env; - if x_opt <> None then mods.env <- Map.add (current_var mods) exports mods.env - -let lookup (mods : modules) x_opt name at = - let exports = - try Map.find (of_var_opt mods x_opt) mods.env with Not_found -> - raise (Eval.Crash (at, - if x_opt = None then "no module defined within script" - else "unknown module " ^ of_var_opt mods x_opt ^ " within script")) - in try NameMap.find name exports with Not_found -> + env.current_mod <- env.current_mod + 1; + env.mods <- Map.add (of_mod_opt env x_opt) exports env.mods; + if x_opt <> None then env.mods <- Map.add (current_mod env) exports env.mods + +let bind_inst (env : env) x_opt exports = + env.current_inst <- env.current_inst + 1; + env.insts <- Map.add (of_inst_opt env x_opt) exports env.insts; + if x_opt <> None then env.insts <- Map.add (current_inst env) exports env.insts + +let find_mod (env : env) x_opt at = + try Map.find (of_mod_opt env x_opt) env.mods with Not_found -> + raise (Eval.Crash (at, + if x_opt = None then "no module defined within script" + else "unknown module " ^ of_mod_opt env x_opt ^ " within script")) + +let find_inst (env : env) x_opt at = + try Map.find (of_inst_opt env x_opt) env.insts with Not_found -> + raise (Eval.Crash (at, + if x_opt = None then "no module instance defined within script" + else "unknown module instance " ^ of_inst_opt env x_opt ^ " within script")) + +let lookup_export (env : env) x_opt name at = + let exports = find_inst env x_opt at in + try NameMap.find name exports with Not_found -> raise (Eval.Crash (at, "unknown export \"" ^ - string_of_name name ^ "\" within module")) + string_of_name name ^ "\" within module isntance")) (* Wrappers *) @@ -275,8 +320,15 @@ let value v = | Num n -> [Const (n @@ v.at) @@ v.at] | Vec s -> [VecConst (s @@ v.at) @@ v.at] | Ref (NullRef ht) -> [RefNull (Match.bot_of_heap_type [] ht) @@ v.at] + | Ref (HostRef n) -> + [ Const (I32 n @@ v.at) @@ v.at; + Call (hostref_idx @@ v.at) @@ v.at; + ] | Ref (Extern.ExternRef (HostRef n)) -> - [Const (I32 n @@ v.at) @@ v.at; Call (hostref_idx @@ v.at) @@ v.at] + [ Const (I32 n @@ v.at) @@ v.at; + Call (hostref_idx @@ v.at) @@ v.at; + ExternConvert Externalize @@ v.at; + ] | Ref _ -> assert false let invoke ft vs at = @@ -294,8 +346,45 @@ let nan_bitmask_of = function | CanonicalNan -> abs_mask_of (* differ from canonical NaN in sign bit *) | ArithmeticNan -> canonical_nan_of (* 1 everywhere canonical NaN is *) +let type_of_num_pat = function + | NumPat num -> Value.type_of_num num.it + | NanPat op -> Value.type_of_op op.it + +let type_of_vec_pat = function + | VecPat vec -> Value.type_of_vec vec + +let type_of_ref_pat = function + | RefPat ref -> type_of_ref ref.it + | RefTypePat ht -> (NoNull, ht) + | NullPat -> (Null, BotHT) + +let rec type_of_result res = + match res.it with + | NumResult pat -> NumT (type_of_num_pat pat) + | VecResult pat -> VecT (type_of_vec_pat pat) + | RefResult pat -> RefT (type_of_ref_pat pat) + | EitherResult rs -> + let ts = List.map type_of_result rs in + List.fold_left (fun t1 t2 -> + if Match.match_val_type [] t1 t2 then t2 else + if Match.match_val_type [] t2 t1 then t1 else + if Match.(top_of_val_type [] t1 = top_of_val_type [] t2) then + Match.top_of_val_type [] t1 + else + BotT (* should really be Top, but we don't have that :) *) + ) (List.hd ts) ts + let assert_return ress ts at = - let test (res, t) = + let locals = ref [] in + let rec test (res, t) = + if + not ( + Match.match_val_type [] t (type_of_result res) || + Match.match_val_type [] (type_of_result res) t + ) + then + [ Br (0l @@ at) @@ at ] + else match res.it with | NumResult (NumPat {it = num; at = at'}) -> let t', reinterpret = reinterpret_of (Value.type_of_op num) in @@ -362,30 +451,51 @@ let assert_return ress ts at = VecTest (V128 (V128.I8x16 V128Op.AllTrue)) @@ at; Test (I32 I32Op.Eqz) @@ at; BrIf (0l @@ at) @@ at ] - | RefResult (RefPat {it = NullRef t; _}) -> + | RefResult (RefPat {it = NullRef _; _}) -> [ RefIsNull @@ at; Test (Value.I32 I32Op.Eqz) @@ at; BrIf (0l @@ at) @@ at ] | RefResult (RefPat {it = HostRef n; _}) -> [ Const (Value.I32 n @@ at) @@ at; Call (hostref_idx @@ at) @@ at; - Call (eq_ref_idx @@ at) @@ at; + Call (eq_ref_idx @@ at) @@ at; + Test (Value.I32 I32Op.Eqz) @@ at; + BrIf (0l @@ at) @@ at ] + | RefResult (RefPat {it = Extern.ExternRef (HostRef n); _}) -> + [ Const (Value.I32 n @@ at) @@ at; + Call (hostref_idx @@ at) @@ at; + ExternConvert Externalize @@ at; + Call (eq_ref_idx @@ at) @@ at; Test (Value.I32 I32Op.Eqz) @@ at; BrIf (0l @@ at) @@ at ] | RefResult (RefPat _) -> assert false + | RefResult (RefTypePat (ExnHT | ExternHT)) -> + [ BrOnNull (0l @@ at) @@ at ] | RefResult (RefTypePat t) -> [ RefTest (NoNull, t) @@ at; Test (I32 I32Op.Eqz) @@ at; BrIf (0l @@ at) @@ at ] | RefResult NullPat -> - (match t with - | RefT _ -> - [ BrOnNull (0l @@ at) @@ at ] - | _ -> - [ Br (0l @@ at) @@ at ] - ) - in [], List.flatten (List.rev_map test (List.combine ress ts)) + [ RefIsNull @@ at; + Test (I32 I32Op.Eqz) @@ at; + BrIf (0l @@ at) @@ at ] + | EitherResult ress -> + let idx = Lib.List32.length !locals in + locals := !locals @ [{ltype = t} @@ res.at]; + [ LocalSet (idx @@ res.at) @@ res.at; + Block (ValBlockType None, + List.map (fun resI -> + Block (ValBlockType None, + [LocalGet (idx @@ resI.at) @@ resI.at] @ + test (resI, t) @ + [Br (1l @@ resI.at) @@ resI.at] + ) @@ resI.at + ) ress @ + [Br (1l @@ at) @@ at] + ) @@ at + ] + in !locals, List.flatten (List.rev_map test (List.combine ress ts)) let i32 = NumT I32T let anyref = RefT (Null, AnyHT) @@ -424,17 +534,32 @@ let wrap item_name wrap_action wrap_assertion at = in let funcs = [{ftype = 0l @@ at; locals; body} @@ at] in let m = {empty_module with types; funcs; imports; exports} @@ at in + (try + Valid.check_module m; (* sanity check *) + with Valid.Invalid _ as exn -> + prerr_endline (string_of_region at ^ + ": internal error in JS converter, invalid wrapper module generated:"); + Sexpr.output stderr 80 (Arrange.module_ m); + raise exn + ); Encode.encode m let is_js_num_type = function - | I32T -> true - | I64T | F32T | F64T -> false + | I32T | I64T -> true + | F32T | F64T -> false + +let is_js_vec_type = function + | _ -> false + +let is_js_ref_type = function + | (_, ExnHT) -> false + | _ -> true let is_js_val_type = function | NumT t -> is_js_num_type t - | VecT _ -> false - | RefT _ -> true + | VecT t -> is_js_vec_type t + | RefT t -> is_js_ref_type t | BotT -> assert false let is_js_global_type = function @@ -482,7 +607,7 @@ let of_num n = let open Value in match n with | I32 i -> I32.to_string_s i - | I64 i -> "int64(\"" ^ I64.to_string_s i ^ "\")" + | I64 i -> I64.to_string_s i ^ "n" | F32 z -> of_float (F32.to_float z) | F64 z -> of_float (F64.to_float z) @@ -524,52 +649,53 @@ let of_ref_pat = function | RefTypePat t -> "\"ref." ^ string_of_heap_type t ^ "\"" | NullPat -> "\"ref.null\"" -let of_result res = +let rec of_result res = match res.it with | NumResult np -> of_num_pat np | VecResult vp -> of_vec_pat vp | RefResult rp -> of_ref_pat rp + | EitherResult ress -> + "[" ^ String.concat ", " (List.map of_result ress) ^ "]" let rec of_definition def = match def.it with - | Textual m -> of_bytes (Encode.encode m) - | Encoded (_, bs) -> of_bytes bs + | Textual (m, _) -> of_bytes (Encode.encode m) + | Encoded (_, bs) -> of_bytes bs.it | Quoted (_, s) -> - try of_definition (snd (Parse.Module.parse_string s)) - with Parse.Syntax _ -> - of_bytes "" + try of_definition (snd (Parse.Module.parse_string ~offset:s.at s.it)) + with Parse.Syntax _ | Custom.Syntax _ -> of_bytes "" -let of_wrapper mods x_opt name wrap_action wrap_assertion at = - let x = of_var_opt mods x_opt in +let of_wrapper env x_opt name wrap_action wrap_assertion at = + let x = of_inst_opt env x_opt in let bs = wrap name wrap_action wrap_assertion at in - "call(instance(" ^ of_bytes bs ^ ", " ^ + "call(instance(module(" ^ of_bytes bs ^ "), " ^ "exports(" ^ x ^ ")), " ^ " \"run\", [])" -let of_action mods act = +let of_action env act = match act.it with | Invoke (x_opt, name, vs) -> - "call(" ^ of_var_opt mods x_opt ^ ", " ^ of_name name ^ ", " ^ + "call(" ^ of_inst_opt env x_opt ^ ", " ^ of_name name ^ ", " ^ "[" ^ String.concat ", " (List.map of_value vs) ^ "])", - (match lookup mods x_opt name act.at with + (match lookup_export env x_opt name act.at with | ExternFuncT dt -> let FuncT (_, out) as ft = as_func_str_type (expand_def_type dt) in if is_js_func_type ft then None else - Some (of_wrapper mods x_opt name (invoke ft vs), out) + Some (of_wrapper env x_opt name (invoke ft vs), out) | _ -> None ) | Get (x_opt, name) -> - "get(" ^ of_var_opt mods x_opt ^ ", " ^ of_name name ^ ")", - (match lookup mods x_opt name act.at with + "get(" ^ of_inst_opt env x_opt ^ ", " ^ of_name name ^ ")", + (match lookup_export env x_opt name act.at with | ExternGlobalT gt when not (is_js_global_type gt) -> let GlobalT (_, t) = gt in - Some (of_wrapper mods x_opt name (get gt), [t]) + Some (of_wrapper env x_opt name (get gt), [t]) | _ -> None ) -let of_assertion' mods act name args wrapper_opt = - let act_js, act_wrapper_opt = of_action mods act in +let of_assertion' env act name args wrapper_opt = + let act_js, act_wrapper_opt = of_action env act in let js = name ^ "(() => " ^ act_js ^ String.concat ", " ("" :: args) ^ ")" in match act_wrapper_opt with | None -> js ^ ";" @@ -580,48 +706,61 @@ let of_assertion' mods act name args wrapper_opt = | Some wrapper -> "run", wrapper in run_name ^ "(() => " ^ act_wrapper (wrapper out) act.at ^ "); // " ^ js -let of_assertion mods ass = +let of_assertion env ass = match ass.it with | AssertMalformed (def, _) -> "assert_malformed(" ^ of_definition def ^ ");" + | AssertMalformedCustom (def, _) -> + "assert_malformed_custom(" ^ of_definition def ^ ");" | AssertInvalid (def, _) -> "assert_invalid(" ^ of_definition def ^ ");" - | AssertUnlinkable (def, _) -> - "assert_unlinkable(" ^ of_definition def ^ ");" - | AssertUninstantiable (def, _) -> - "assert_uninstantiable(" ^ of_definition def ^ ");" + | AssertInvalidCustom (def, _) -> + "assert_invalid_custom(" ^ of_definition def ^ ");" + | AssertUnlinkable (x_opt, _) -> + "assert_unlinkable(" ^ of_mod_opt env x_opt ^ ");" + | AssertUninstantiable (x_opt, _) -> + "assert_uninstantiable(" ^ of_mod_opt env x_opt ^ ");" | AssertReturn (act, ress) -> - of_assertion' mods act "assert_return" (List.map of_result ress) + of_assertion' env act "assert_return" (List.map of_result ress) (Some (assert_return ress)) | AssertTrap (act, _) -> - of_assertion' mods act "assert_trap" [] None + of_assertion' env act "assert_trap" [] None | AssertExhaustion (act, _) -> - of_assertion' mods act "assert_exhaustion" [] None + of_assertion' env act "assert_exhaustion" [] None | AssertException act -> - of_assertion' mods act "assert_exception" [] None + of_assertion' env act "assert_exception" [] None + | AssertSuspension (act, _) -> + of_assertion' env act "assert_suspension" [] None -let of_command mods cmd = +let of_command env cmd = "\n// " ^ Filename.basename cmd.at.left.file ^ ":" ^ string_of_int cmd.at.left.line ^ "\n" ^ match cmd.it with | Module (x_opt, def) -> let rec unquote def = match def.it with - | Textual m -> m - | Encoded (_, bs) -> Decode.decode "binary" bs - | Quoted (_, s) -> unquote (snd (Parse.Module.parse_string s)) - in bind mods x_opt (unquote def); - "let " ^ current_var mods ^ " = instance(" ^ of_definition def ^ ");\n" ^ + | Textual (m, _) -> m + | Encoded (name, bs) -> Decode.decode name bs.it + | Quoted (_, s) -> + unquote (snd (Parse.Module.parse_string ~offset:s.at s.it)) + in bind_mod env x_opt (unquote def); + "let " ^ current_mod env ^ " = module(" ^ of_definition def ^ ");\n" ^ (if x_opt = None then "" else - "let " ^ of_var_opt mods x_opt ^ " = " ^ current_var mods ^ ";\n") + "let " ^ of_mod_opt env x_opt ^ " = " ^ current_mod env ^ ";\n") + | Instance (x1_opt, x2_opt) -> + let exports = find_mod env x2_opt cmd.at in + bind_inst env x1_opt exports; + "let " ^ current_inst env ^ " = instance(" ^ of_mod_opt env x2_opt ^ ");\n" ^ + (if x1_opt = None then "" else + "let " ^ of_inst_opt env x1_opt ^ " = " ^ current_inst env ^ ";\n") | Register (name, x_opt) -> - "register(" ^ of_name name ^ ", " ^ of_var_opt mods x_opt ^ ")\n" + "register(" ^ of_name name ^ ", " ^ of_inst_opt env x_opt ^ ")\n" | Action act -> - of_assertion' mods act "run" [] None ^ "\n" + of_assertion' env act "run" [] None ^ "\n" | Assertion ass -> - of_assertion mods ass ^ "\n" + of_assertion env ass ^ "\n" | Meta _ -> assert false let of_script scr = (if !Flags.harness then harness else "") ^ - String.concat "" (List.map (of_command (modules ())) scr) + String.concat "" (List.map (of_command (env ())) scr) diff --git a/interpreter/script/run.ml b/interpreter/script/run.ml index fe4bcec15..674ccc1b5 100644 --- a/interpreter/script/run.ml +++ b/interpreter/script/run.ml @@ -43,7 +43,7 @@ let dispatch_file_ext on_binary on_sexpr on_script_binary on_script on_js file = let create_binary_file file _ get_module = trace ("Encoding (" ^ file ^ ")..."); - let s = Encode.encode (get_module ()) in + let s = Encode.encode_with_custom (get_module ()) in let oc = open_out_bin file in try trace "Writing..."; @@ -55,7 +55,7 @@ let create_sexpr_file file _ get_module = trace ("Writing (" ^ file ^ ")..."); let oc = open_out file in try - Print.module_ oc !Flags.width (get_module ()); + Print.module_with_custom oc !Flags.width (get_module ()); close_out oc with exn -> close_out oc; raise exn @@ -87,7 +87,7 @@ let output_file = let output_stdout get_module = trace "Printing..."; - Print.module_ stdout !Flags.width (get_module ()) + Print.module_with_custom stdout !Flags.width (get_module ()) (* Input *) @@ -106,13 +106,17 @@ let input_from get_script run = with | Decode.Code (at, msg) -> error at "decoding error" msg | Parse.Syntax (at, msg) -> error at "syntax error" msg - | Valid.Invalid (at, msg) -> error at "invalid module" msg + | Valid.Invalid (at, msg) -> error at "validation error" msg + | Custom.Code (at, msg) -> error at "custom section decoding error" msg + | Custom.Syntax (at, msg) -> error at "custom annotation syntax error" msg + | Custom.Invalid (at, msg) -> error at "custom validation error" msg | Import.Unknown (at, msg) -> error at "link failure" msg | Eval.Link (at, msg) -> error at "link failure" msg | Eval.Trap (at, msg) -> error at "runtime trap" msg | Eval.Exhaustion (at, msg) -> error at "resource exhaustion" msg | Eval.Crash (at, msg) -> error at "runtime crash" msg | Eval.Exception (at, msg) -> error at "uncaught exception" msg + | Eval.Suspension (at, msg) -> error at "suspension error" msg | Encode.Code (at, msg) -> error at "encoding error" msg | Script.Error (at, msg) -> error at "script error" msg | IO (at, msg) -> error at "i/o error" msg @@ -133,7 +137,8 @@ let input_sexpr name lexbuf run = let input_binary name buf run = let open Source in input_from (fun () -> - [Module (None, Encoded (name, buf) @@ no_region) @@ no_region]) run + [Module (None, Encoded (name, buf @@ no_region) @@ no_region) @@ no_region] + ) run let input_sexpr_file input file run = trace ("Loading (" ^ file ^ ")..."); @@ -230,16 +235,6 @@ let string_of_nan = function | CanonicalNan -> "nan:canonical" | ArithmeticNan -> "nan:arithmetic" -let type_of_result r = - let open Types in - match r with - | NumResult (NumPat n) -> NumT (Value.type_of_num n.it) - | NumResult (NanPat n) -> NumT (Value.type_of_num n.it) - | VecResult (VecPat v) -> VecT (Value.type_of_vec v) - | RefResult (RefPat r) -> RefT (Value.type_of_ref r.it) - | RefResult (RefTypePat t) -> RefT (NoNull, t) (* assume closed *) - | RefResult (NullPat) -> RefT (Null, ExternHT) - let string_of_num_pat (p : num_pat) = match p with | NumPat n -> Value.string_of_num n.it @@ -259,16 +254,38 @@ let string_of_ref_pat (p : ref_pat) = | RefTypePat t -> Types.string_of_heap_type t | NullPat -> "null" -let string_of_result r = - match r with +let rec string_of_result r = + match r.it with | NumResult np -> string_of_num_pat np | VecResult vp -> string_of_vec_pat vp | RefResult rp -> string_of_ref_pat rp + | EitherResult rs -> + "(" ^ String.concat " | " (List.map string_of_result rs) ^ ")" let string_of_results = function | [r] -> string_of_result r | rs -> "[" ^ String.concat " " (List.map string_of_result rs) ^ "]" +let rec type_of_result r = + let open Types in + match r.it with + | NumResult (NumPat n) -> NumT (Value.type_of_num n.it) + | NumResult (NanPat n) -> NumT (Value.type_of_num n.it) + | VecResult (VecPat v) -> VecT (Value.type_of_vec v) + | RefResult (RefPat r) -> RefT (Value.type_of_ref r.it) + | RefResult (RefTypePat t) -> RefT (NoNull, t) (* assume closed *) + | RefResult (NullPat) -> RefT (Null, ExternHT) + | EitherResult rs -> + let ts = List.map type_of_result rs in + List.fold_left (fun t1 t2 -> + if Match.match_val_type [] t1 t2 then t2 else + if Match.match_val_type [] t2 t1 then t1 else + if Match.(top_of_val_type [] t1 = top_of_val_type [] t2) then + Match.top_of_val_type [] t1 + else + BotT (* should really be Top, but we don't have that :) *) + ) (List.hd ts) ts + let print_results rs = let ts = List.map type_of_result rs in Printf.printf "%s : %s\n%!" @@ -281,15 +298,18 @@ module Map = Map.Make(String) let quote : script ref = ref [] let scripts : script Map.t ref = ref Map.empty -let modules : Ast.module_ Map.t ref = ref Map.empty +let modules : (Ast.module_ * Custom.section list) Map.t ref = ref Map.empty let instances : Instance.module_inst Map.t ref = ref Map.empty let registry : Instance.module_inst Map.t ref = ref Map.empty -let bind map x_opt y = +let bind category map x_opt y = let map' = match x_opt with | None -> !map - | Some x -> Map.add x.it y !map + | Some x -> + if Map.mem x.it !map then + IO.error x.at (category ^ " " ^ x.it ^ " already defined"); + Map.add x.it y !map in map := Map.add "" y map' let lookup category map x_opt at = @@ -301,7 +321,7 @@ let lookup category map x_opt at = let lookup_script = lookup "script" scripts let lookup_module = lookup "module" modules -let lookup_instance = lookup "module" instances +let lookup_instance = lookup "module instance" instances let lookup_registry module_name item_name _t = match Instance.export (Map.find module_name !registry) item_name with @@ -311,15 +331,15 @@ let lookup_registry module_name item_name _t = (* Running *) -let rec run_definition def : Ast.module_ = +let rec run_definition def : Ast.module_ * Custom.section list = match def.it with - | Textual m -> m + | Textual (m, cs) -> m, cs | Encoded (name, bs) -> trace "Decoding..."; - Decode.decode name bs + Decode.decode_with_custom name bs.it | Quoted (_, s) -> trace "Parsing quote..."; - let _, def' = Parse.Module.parse_string s in + let _, def' = Parse.Module.parse_string ~offset:s.at s.it in run_definition def' let run_action act : Value.t list = @@ -394,22 +414,24 @@ let assert_ref_pat r p = | RefTypePat Types.StructHT, Aggr.StructRef _ | RefTypePat Types.ArrayHT, Aggr.ArrayRef _ -> true | RefTypePat Types.FuncHT, Instance.FuncRef _ + | RefTypePat Types.ExnHT, Exn.ExnRef _ | RefTypePat Types.ExternHT, _ -> true | NullPat, Value.NullRef _ -> true | _ -> false -let assert_pat v r = +let rec assert_result v r = let open Value in - match v, r with + match v, r.it with | Num n, NumResult np -> assert_num_pat n np | Vec v, VecResult vp -> assert_vec_pat v vp | Ref r, RefResult rp -> assert_ref_pat r rp + | _, EitherResult rs -> List.exists (assert_result v) rs | _, _ -> false -let assert_result at got expect = +let assert_results at got expect = if List.length got <> List.length expect || - List.exists2 (fun v r -> not (assert_pat v r)) got expect + not (List.for_all2 assert_result got expect) then begin print_string "Result: "; print_values got; print_string "Expect: "; print_results expect; @@ -436,21 +458,40 @@ let run_assertion ass = | _ -> Assert.error ass.at "expected decoding/parsing error" ) + | AssertMalformedCustom (def, re) -> + trace "Asserting malformed custom..."; + (match ignore (run_definition def) with + | exception Custom.Syntax (_, msg) -> + assert_message ass.at "annotation parsing" msg re + | _ -> Assert.error ass.at "expected custom decoding/parsing error" + ) + | AssertInvalid (def, re) -> trace "Asserting invalid..."; (match - let m = run_definition def in - Valid.check_module m + let m, cs = run_definition def in + Valid.check_module_with_custom (m, cs) with | exception Valid.Invalid (_, msg) -> assert_message ass.at "validation" msg re | _ -> Assert.error ass.at "expected validation error" ) - | AssertUnlinkable (def, re) -> + | AssertInvalidCustom (def, re) -> + trace "Asserting invalid custom..."; + (match + let m, cs = run_definition def in + Valid.check_module_with_custom (m, cs) + with + | exception Custom.Invalid (_, msg) -> + assert_message ass.at "custom validation" msg re + | _ -> Assert.error ass.at "expected custom validation error" + ) + + | AssertUnlinkable (x_opt, re) -> trace "Asserting unlinkable..."; - let m = run_definition def in - if not !Flags.unchecked then Valid.check_module m; + let m, cs = lookup_module x_opt ass.at in + if not !Flags.unchecked then Valid.check_module_with_custom (m, cs); (match let imports = Import.link m in ignore (Eval.init m imports) @@ -460,10 +501,10 @@ let run_assertion ass = | _ -> Assert.error ass.at "expected linking error" ) - | AssertUninstantiable (def, re) -> + | AssertUninstantiable (x_opt, re) -> trace "Asserting trap..."; - let m = run_definition def in - if not !Flags.unchecked then Valid.check_module m; + let m, cs = lookup_module x_opt ass.at in + if not !Flags.unchecked then Valid.check_module_with_custom (m, cs); (match let imports = Import.link m in ignore (Eval.init m imports) @@ -476,8 +517,7 @@ let run_assertion ass = | AssertReturn (act, rs) -> trace ("Asserting return..."); let got_vs = run_action act in - let expect_rs = List.map (fun r -> r.it) rs in - assert_result ass.at got_vs expect_rs + assert_results ass.at got_vs rs | AssertException act -> trace ("Asserting exception..."); @@ -493,6 +533,13 @@ let run_assertion ass = | _ -> Assert.error ass.at "expected runtime error" ) + | AssertSuspension (act, re) -> + trace ("Asserting suspension..."); + (match run_action act with + | exception Eval.Suspension (_, msg) -> assert_message ass.at "runtime" msg re + | _ -> Assert.error ass.at "expected suspension" + ) + | AssertExhaustion (act, re) -> trace ("Asserting exhaustion..."); (match run_action act with @@ -505,22 +552,26 @@ let rec run_command cmd = match cmd.it with | Module (x_opt, def) -> quote := cmd :: !quote; - let m = run_definition def in + let m, cs = run_definition def in if not !Flags.unchecked then begin trace "Checking..."; - Valid.check_module m; + Valid.check_module_with_custom (m, cs); if !Flags.print_sig then begin trace "Signature:"; print_module x_opt m end end; - bind scripts x_opt [cmd]; - bind modules x_opt m; + bind "module" modules x_opt (m, cs); + bind "script" scripts x_opt [cmd] + + | Instance (x1_opt, x2_opt) -> + quote := cmd :: !quote; + let m, cs = lookup_module x2_opt cmd.at in if not !Flags.dry then begin trace "Initializing..."; let imports = Import.link m in let inst = Eval.init m imports in - bind instances x_opt inst + bind "instance" instances x1_opt inst end | Register (name, x_opt) -> @@ -552,17 +603,17 @@ and run_meta cmd = match cmd.it with | Script (x_opt, script) -> run_quote_script script; - bind scripts x_opt (lookup_script None cmd.at) + bind "script" scripts x_opt (lookup_script None cmd.at) | Input (x_opt, file) -> (try if not (input_file file run_quote_script) then Abort.error cmd.at "aborting" with Sys_error msg -> IO.error cmd.at msg); - bind scripts x_opt (lookup_script None cmd.at); + bind "script" scripts x_opt (lookup_script None cmd.at); if x_opt <> None then begin - bind modules x_opt (lookup_module None cmd.at); + bind "module" modules x_opt (lookup_module None cmd.at); if not !Flags.dry then begin - bind instances x_opt (lookup_instance None cmd.at) + bind "instance" instances x_opt (lookup_instance None cmd.at) end end @@ -584,7 +635,7 @@ and run_quote_script script = let save_quote = !quote in quote := []; (try run_script script with exn -> quote := save_quote; raise exn); - bind scripts None (List.rev !quote); + bind "script" scripts None (List.rev !quote); quote := !quote @ save_quote let run_file file = input_file file run_script diff --git a/interpreter/script/script.ml b/interpreter/script/script.ml index e7c07144d..d9c57e0f2 100644 --- a/interpreter/script/script.ml +++ b/interpreter/script/script.ml @@ -7,9 +7,9 @@ type literal = Value.t Source.phrase type definition = definition' Source.phrase and definition' = - | Textual of Ast.module_ - | Encoded of string * string - | Quoted of string * string + | Textual of Ast.module_ * Custom.section list + | Encoded of string * string Source.phrase + | Quoted of string * string Source.phrase type action = action' Source.phrase and action' = @@ -37,21 +37,26 @@ and result' = | NumResult of num_pat | VecResult of vec_pat | RefResult of ref_pat + | EitherResult of result list type assertion = assertion' Source.phrase and assertion' = | AssertMalformed of definition * string + | AssertMalformedCustom of definition * string | AssertInvalid of definition * string - | AssertUnlinkable of definition * string - | AssertUninstantiable of definition * string + | AssertInvalidCustom of definition * string + | AssertUnlinkable of var option * string + | AssertUninstantiable of var option * string | AssertReturn of action * result list - | AssertException of action | AssertTrap of action * string + | AssertException of action + | AssertSuspension of action * string | AssertExhaustion of action * string type command = command' Source.phrase and command' = | Module of var option * definition + | Instance of var option * var option | Register of Ast.name * var option | Action of action | Assertion of assertion diff --git a/interpreter/syntax/ast.ml b/interpreter/syntax/ast.ml index 5d5ac6dc6..30ea07dcd 100644 --- a/interpreter/syntax/ast.ml +++ b/interpreter/syntax/ast.ml @@ -64,13 +64,19 @@ struct | AddSatS | AddSatU | SubSatS | SubSatU | DotS | Q15MulRSatS | ExtMulLowS | ExtMulHighS | ExtMulLowU | ExtMulHighU | Swizzle | Shuffle of int list | NarrowS | NarrowU + | RelaxedSwizzle | RelaxedQ15MulRS | RelaxedDot type fbinop = Add | Sub | Mul | Div | Min | Max | Pmin | Pmax + | RelaxedMin | RelaxedMax + type iternop = RelaxedLaneselect | RelaxedDotAccum + type fternop = RelaxedMadd | RelaxedNmadd type irelop = Eq | Ne | LtS | LtU | LeS | LeU | GtS | GtU | GeS | GeU type frelop = Eq | Ne | Lt | Le | Gt | Ge type icvtop = ExtendLowS | ExtendLowU | ExtendHighS | ExtendHighU | ExtAddPairwiseS | ExtAddPairwiseU | TruncSatSF32x4 | TruncSatUF32x4 | TruncSatSZeroF64x2 | TruncSatUZeroF64x2 + | RelaxedTruncSF32x4 | RelaxedTruncUF32x4 + | RelaxedTruncSZeroF64x2 | RelaxedTruncUZeroF64x2 type fcvtop = DemoteZeroF64x2 | PromoteLowF32x4 | ConvertSI32x4 | ConvertUI32x4 type ishiftop = Shl | ShrS | ShrU @@ -84,6 +90,7 @@ struct type testop = (itestop, itestop, itestop, itestop, void, void) V128.laneop type unop = (iunop, iunop, iunop, iunop, funop, funop) V128.laneop type binop = (ibinop, ibinop, ibinop, ibinop, fbinop, fbinop) V128.laneop + type ternop = (iternop, iternop, iternop, iternop, fternop, fternop) V128.laneop type relop = (irelop, irelop, irelop, irelop, frelop, frelop) V128.laneop type cvtop = (icvtop, icvtop, icvtop, icvtop, fcvtop, fcvtop) V128.laneop type shiftop = (ishiftop, ishiftop, ishiftop, ishiftop, void, void) V128.laneop @@ -108,6 +115,7 @@ type vec_testop = (V128Op.testop) Value.vecop type vec_relop = (V128Op.relop) Value.vecop type vec_unop = (V128Op.unop) Value.vecop type vec_binop = (V128Op.binop) Value.vecop +type vec_ternop = (V128Op.ternop) Value.vecop type vec_cvtop = (V128Op.cvtop) Value.vecop type vec_shiftop = (V128Op.shiftop) Value.vecop type vec_bitmaskop = (V128Op.bitmaskop) Value.vecop @@ -119,7 +127,7 @@ type vec_splatop = (V128Op.splatop) Value.vecop type vec_extractop = (V128Op.extractop) Value.vecop type vec_replaceop = (V128Op.replaceop) Value.vecop -type ('t, 'p) memop = {ty : 't; align : int; offset : int32; pack : 'p} +type ('t, 'p) memop = {ty : 't; align : int; offset : int64; pack : 'p} type loadop = (num_type, (pack_size * extension) option) memop type storeop = (num_type, pack_size option) memop @@ -139,6 +147,7 @@ type vec = Value.vec Source.phrase type name = Utf8.unicode type block_type = VarBlockType of idx | ValBlockType of val_type option +type hdl = OnLabel of idx | OnSwitch type instr = instr' Source.phrase and instr' = @@ -163,6 +172,13 @@ and instr' = | ReturnCall of idx (* tail-call function *) | ReturnCallRef of idx (* tail call through reference *) | ReturnCallIndirect of idx * idx (* tail-call function through table *) + | ContNew of idx (* create continuation *) + | ContBind of idx * idx (* bind continuation arguments *) + | Suspend of idx (* suspend continuation *) + | Resume of idx * (idx * hdl) list (* resume continuation *) + | ResumeThrow of idx * idx * (idx * hdl) list (* abort continuation *) + | ResumeThrowRef of idx * (idx * hdl) list (* abort continuation *) + | Switch of idx * idx (* direct switch continuation *) | Throw of idx (* throw exception *) | ThrowRef (* rethrow exception *) | TryTable of block_type * catch list * instr list (* handle exceptions *) @@ -226,6 +242,7 @@ and instr' = | VecCompare of vec_relop (* vector comparison *) | VecUnary of vec_unop (* unary vector operator *) | VecBinary of vec_binop (* binary vector operator *) + | VecTernary of vec_ternop (* ternary vector operator *) | VecConvert of vec_cvtop (* vector conversion *) | VecShift of vec_shiftop (* vector shifts *) | VecBitmask of vec_bitmaskop (* vector masking *) @@ -271,6 +288,15 @@ and func' = } +(* Tags *) + +type tag = tag' Source.phrase +and tag' = +{ + tgtype : idx; +} + + (* Tables & Memories *) type table = table' Source.phrase @@ -286,13 +312,6 @@ and memory' = mtype : memory_type; } -type tag = tag' Source.phrase -and tag' = -{ - tgtype : idx; -} - - type segment_mode = segment_mode' Source.phrase and segment_mode' = | Passive @@ -399,6 +418,9 @@ let def_types_of (m : module_) : def_type list = dts @ List.map (subst_def_type (subst_of dts)) (roll_def_types x rt) ) [] rts +let ht (m : module_) (x : idx) : heap_type = + VarHT (StatX x.it) + let import_type_of (m : module_) (im : import) : import_type = let {idesc; module_name; item_name} = im.it in let dts = def_types_of m in diff --git a/interpreter/syntax/free.ml b/interpreter/syntax/free.ml index 082c9d397..ef6a181f8 100644 --- a/interpreter/syntax/free.ml +++ b/interpreter/syntax/free.ml @@ -81,6 +81,7 @@ let heap_type = function | FuncHT | NoFuncHT -> empty | ExnHT | NoExnHT -> empty | ExternHT | NoExternHT -> empty + | ContHT | NoContHT -> empty | VarHT x -> var_type x | DefHT _ct -> empty (* assume closed *) | BotHT -> empty @@ -94,6 +95,9 @@ let val_type = function | RefT t -> ref_type t | BotT -> empty +(* let func_type (FuncT (ins, out)) = list val_type ins ++ list val_type out *) +let cont_type (ContT ht) = heap_type ht + let pack_type t = empty let storage_type = function @@ -110,6 +114,7 @@ let str_type = function | DefStructT st -> struct_type st | DefArrayT at -> array_type at | DefFuncT ft -> func_type ft + | DefContT ct -> cont_type ct let sub_type = function | SubT (_fin, hts, st) -> list heap_type hts ++ str_type st @@ -121,8 +126,8 @@ let def_type = function | DefT (rt, _i) -> rec_type rt let global_type (GlobalT (_mut, t)) = val_type t -let table_type (TableT (_lim, t)) = ref_type t -let memory_type (MemoryT (_lim)) = empty +let table_type (TableT (_at, _lim, t)) = ref_type t +let memory_type (MemoryT (_at, _lim)) = empty let tag_type (TagT dt) = def_type dt let extern_type = function @@ -130,12 +135,16 @@ let extern_type = function | ExternTableT tt -> table_type tt | ExternMemoryT mt -> memory_type mt | ExternGlobalT gt -> global_type gt - | ExternTagT tt -> tag_type tt + | ExternTagT et -> tag_type et let block_type = function | VarBlockType x -> types (idx x) | ValBlockType t -> opt val_type t +let hdl = function + | OnLabel x -> labels (idx x) + | OnSwitch -> empty + let rec instr (e : instr) = match e.it with | Unreachable | Nop | Drop -> empty @@ -168,7 +177,14 @@ let rec instr (e : instr) = | Call x | ReturnCall x -> funcs (idx x) | CallRef x | ReturnCallRef x -> types (idx x) | CallIndirect (x, y) | ReturnCallIndirect (x, y) -> - tables (idx x) ++ types (idx y) + tables (idx x) ++ types (idx y) + | ContNew x -> types (idx x) + | ContBind (x, y) -> types (idx x) ++ types (idx y) + | ResumeThrow (x, y, xys) -> types (idx x) ++ tags (idx y) ++ list (fun (x, y) -> tags (idx x) ++ hdl y) xys + | ResumeThrowRef (x, xys) -> types (idx x) ++ list (fun (x, y) -> tags (idx x) ++ hdl y) xys + | Resume (x, xys) -> types (idx x) ++ list (fun (x, y) -> tags (idx x) ++ hdl y) xys + | Suspend x -> tags (idx x) + | Switch (x, z) -> types (idx x) ++ tags (idx z) | Throw x -> tags (idx x) | ThrowRef -> empty | TryTable (bt, cs, es) -> @@ -187,7 +203,8 @@ let rec instr (e : instr) = | MemoryCopy (x, y) -> memories (idx x) ++ memories (idx y) | MemoryInit (x, y) -> memories (idx x) ++ datas (idx y) | DataDrop x -> datas (idx x) - | VecConst _ | VecTest _ | VecUnary _ | VecBinary _ | VecCompare _ + | VecConst _ | VecTest _ + | VecUnary _ | VecBinary _ | VecTernary _ | VecCompare _ | VecConvert _ | VecShift _ | VecBitmask _ | VecTestBits _ | VecUnaryBits _ | VecBinaryBits _ | VecTernaryBits _ | VecSplat _ | VecExtract _ | VecReplace _ -> @@ -208,7 +225,7 @@ let func (f : func) = {(types (idx f.it.ftype) ++ block f.it.body) with locals = Set.empty} let table (t : table) = table_type t.it.ttype ++ const t.it.tinit let memory (m : memory) = memory_type m.it.mtype -let tag (t : tag) = empty +let tag (e : tag) = empty let segment_mode f (m : segment_mode) = match m.it with @@ -237,7 +254,7 @@ let import_desc (d : import_desc) = | TableImport tt -> table_type tt | MemoryImport mt -> memory_type mt | GlobalImport gt -> global_type gt - | TagImport x -> types (idx x) + | TagImport et -> types (idx et) let export (e : export) = export_desc e.it.edesc let import (i : import) = import_desc i.it.idesc @@ -249,6 +266,7 @@ let module_ (m : module_) = list global m.it.globals ++ list table m.it.tables ++ list memory m.it.memories ++ + list tag m.it.tags ++ list func m.it.funcs ++ opt start m.it.start ++ list elem m.it.elems ++ diff --git a/interpreter/syntax/free.mli b/interpreter/syntax/free.mli index cb76c0fb5..3c18c665c 100644 --- a/interpreter/syntax/free.mli +++ b/interpreter/syntax/free.mli @@ -26,6 +26,7 @@ val func_type : Types.func_type -> t val global_type : Types.global_type -> t val table_type : Types.table_type -> t val memory_type : Types.memory_type -> t +val tag_type : Types.tag_type -> t val extern_type : Types.extern_type -> t val str_type : Types.str_type -> t diff --git a/interpreter/syntax/operators.ml b/interpreter/syntax/operators.ml index 9a467b1cb..42fa82cc9 100644 --- a/interpreter/syntax/operators.ml +++ b/interpreter/syntax/operators.ml @@ -15,6 +15,10 @@ let v128_const n = VecConst (V128 n.it @@ n.at) let ref_null t = RefNull t let ref_func x = RefFunc x +let at_const = function + | I32AT -> fun n -> i32_const (I32_convert.wrap_i64 n.it @@ n.at) + | I64AT -> i64_const + let unreachable = Unreachable let nop = Nop let drop = Drop @@ -45,6 +49,13 @@ let return_call x = ReturnCall x let return_call_ref x = ReturnCallRef x let return_call_indirect x y = ReturnCallIndirect (x, y) +let cont_new x = ContNew x +let cont_bind x y = ContBind (x, y) +let suspend x = Suspend x +let resume x xys = Resume (x, xys) +let resume_throw x y xys = ResumeThrow (x, y, xys) +let resume_throw_ref x xys = ResumeThrowRef (x, xys) +let switch x y = Switch (x, y) let throw x = Throw x let throw_ref = ThrowRef let try_table bt cs es = TryTable (bt, cs, es) @@ -560,3 +571,24 @@ let f64x2_pmax = VecBinary (V128 (F64x2 V128Op.Pmax)) let f64x2_promote_low_f32x4 = VecConvert (V128 (F64x2 V128Op.PromoteLowF32x4)) let f64x2_convert_low_i32x4_s = VecConvert (V128 (F64x2 V128Op.ConvertSI32x4)) let f64x2_convert_low_i32x4_u = VecConvert (V128 (F64x2 V128Op.ConvertUI32x4)) + +let i8x16_relaxed_swizzle = VecBinary (V128 (I8x16 V128Op.RelaxedSwizzle)) +let i8x16_relaxed_laneselect = VecTernary (V128 (I8x16 V128Op.RelaxedLaneselect)) +let i16x8_relaxed_q15mulr_s = VecBinary (V128 (I16x8 V128Op.RelaxedQ15MulRS)) +let i16x8_relaxed_laneselect = VecTernary (V128 (I16x8 V128Op.RelaxedLaneselect)) +let i32x4_relaxed_trunc_f32x4_s = VecConvert (V128 (I32x4 V128Op.RelaxedTruncSF32x4)) +let i32x4_relaxed_trunc_f32x4_u = VecConvert (V128 (I32x4 V128Op.RelaxedTruncUF32x4)) +let i32x4_relaxed_trunc_f64x2_s_zero = VecConvert (V128 (I32x4 V128Op.RelaxedTruncSZeroF64x2)) +let i32x4_relaxed_trunc_f64x2_u_zero = VecConvert (V128 (I32x4 V128Op.RelaxedTruncUZeroF64x2)) +let i32x4_relaxed_laneselect = VecTernary (V128 (I32x4 V128Op.RelaxedLaneselect)) +let i64x2_relaxed_laneselect = VecTernary (V128 (I64x2 V128Op.RelaxedLaneselect)) +let f32x4_relaxed_madd = VecTernary (V128 (F32x4 V128Op.RelaxedMadd)) +let f32x4_relaxed_nmadd = VecTernary (V128 (F32x4 V128Op.RelaxedNmadd)) +let f32x4_relaxed_min = VecBinary (V128 (F32x4 V128Op.RelaxedMin)) +let f32x4_relaxed_max = VecBinary (V128 (F32x4 V128Op.RelaxedMax)) +let f64x2_relaxed_madd = VecTernary (V128 (F64x2 V128Op.RelaxedMadd)) +let f64x2_relaxed_nmadd = VecTernary (V128 (F64x2 V128Op.RelaxedNmadd)) +let f64x2_relaxed_min = VecBinary (V128 (F64x2 V128Op.RelaxedMin)) +let f64x2_relaxed_max = VecBinary (V128 (F64x2 V128Op.RelaxedMax)) +let i16x8_relaxed_dot_i8x16_i7x16_s = VecBinary (V128 (I16x8 V128Op.RelaxedDot)) +let i32x4_relaxed_dot_i8x16_i7x16_add_s = VecTernary (V128 (I32x4 V128Op.RelaxedDotAccum)) diff --git a/interpreter/syntax/types.ml b/interpreter/syntax/types.ml index dd233c9f7..9acb88d4c 100644 --- a/interpreter/syntax/types.ml +++ b/interpreter/syntax/types.ml @@ -8,10 +8,11 @@ type null = NoNull | Null type mut = Cons | Var type init = Set | Unset type final = NoFinal | Final -type 'a limits = {min : 'a; max : 'a option} +type limits = {min : int64; max : int64 option} type var = StatX of type_idx | RecX of int32 +type addr_type = I32AT | I64AT type num_type = I32T | I64T | F32T | F64T type vec_type = V128T type heap_type = @@ -19,6 +20,7 @@ type heap_type = | FuncHT | NoFuncHT | ExnHT | NoExnHT | ExternHT | NoExternHT + | ContHT | NoContHT | VarHT of var | DefHT of def_type | BotHT @@ -34,21 +36,23 @@ and field_type = FieldT of mut * storage_type and struct_type = StructT of field_type list and array_type = ArrayT of field_type and func_type = FuncT of result_type * result_type +and cont_type = ContT of heap_type and str_type = | DefStructT of struct_type | DefArrayT of array_type | DefFuncT of func_type + | DefContT of cont_type and sub_type = SubT of final * heap_type list * str_type and rec_type = RecT of sub_type list and def_type = DefT of rec_type * int32 -type table_type = TableT of Int32.t limits * ref_type -type memory_type = MemoryT of Int32.t limits +type table_type = TableT of addr_type * limits * ref_type +type memory_type = MemoryT of addr_type * limits type global_type = GlobalT of mut * val_type -type tag_type = TagT of def_type type local_type = LocalT of init * val_type +type tag_type = TagT of def_type type extern_type = | ExternFuncT of def_type | ExternTableT of table_type @@ -110,34 +114,6 @@ let defaultable = function | BotT -> assert false -(* Projections *) - -let unpacked_storage_type = function - | ValStorageT t -> t - | PackStorageT _ -> NumT I32T - -let unpacked_field_type (FieldT (_mut, t)) = unpacked_storage_type t - - -let as_func_str_type (st : str_type) : func_type = - match st with - | DefFuncT ft -> ft - | _ -> assert false - -let as_struct_str_type (st : str_type) : struct_type = - match st with - | DefStructT st -> st - | _ -> assert false - -let as_array_str_type (st : str_type) : array_type = - match st with - | DefArrayT at -> at - | _ -> assert false - -let extern_type_of_import_type (ImportT (et, _, _)) = et -let extern_type_of_export_type (ExportT (et, _)) = et - - (* Filters *) let funcs = List.filter_map (function ExternFuncT ft -> Some ft | _ -> None) @@ -155,6 +131,7 @@ let subst_of dts = function | StatX x -> DefHT (Lib.List32.nth dts x) | RecX i -> VarHT (RecX i) +let subst_addr_type s t = t let subst_num_type s t = t @@ -173,6 +150,8 @@ let subst_heap_type s = function | NoExnHT -> NoExnHT | ExternHT -> ExternHT | NoExternHT -> NoExternHT + | ContHT -> ContHT + | NoContHT -> NoContHT | VarHT x -> s x | DefHT dt -> DefHT dt (* assume closed *) | BotHT -> BotHT @@ -206,10 +185,14 @@ let subst_array_type s = function let subst_func_type s = function | FuncT (ts1, ts2) -> FuncT (subst_result_type s ts1, subst_result_type s ts2) +let subst_cont_type s = function + | ContT ht -> ContT (subst_heap_type s ht) + let subst_str_type s = function | DefStructT st -> DefStructT (subst_struct_type s st) | DefArrayT at -> DefArrayT (subst_array_type s at) | DefFuncT ft -> DefFuncT (subst_func_type s ft) + | DefContT ct -> DefContT (subst_cont_type s ct) let subst_sub_type s = function | SubT (fin, hts, st) -> @@ -223,10 +206,10 @@ let subst_def_type s = function let subst_memory_type s = function - | MemoryT lim -> MemoryT lim + | MemoryT (at, lim) -> MemoryT (subst_addr_type s at, lim) let subst_table_type s = function - | TableT (lim, t) -> TableT (lim, subst_ref_type s t) + | TableT (at, lim, t) -> TableT (subst_addr_type s at, lim, subst_ref_type s t) let subst_global_type s = function | GlobalT (mut, t) -> GlobalT (mut, subst_val_type s t) @@ -239,8 +222,7 @@ let subst_extern_type s = function | ExternTableT tt -> ExternTableT (subst_table_type s tt) | ExternMemoryT mt -> ExternMemoryT (subst_memory_type s mt) | ExternGlobalT gt -> ExternGlobalT (subst_global_type s gt) - | ExternTagT tt -> ExternTagT (subst_tag_type s tt) - + | ExternTagT et -> ExternTagT (subst_tag_type s et) let subst_export_type s = function | ExportT (et, name) -> ExportT (subst_extern_type s et, name) @@ -290,6 +272,60 @@ let expand_def_type (dt : def_type) : str_type = st +(* Conversions & Projections *) + +let num_type_of_addr_type = function + | I32AT -> I32T + | I64AT -> I64T + +let addr_type_of_num_type = function + | I32T -> I32AT + | I64T -> I64AT + | _ -> assert false + +let unpacked_storage_type = function + | ValStorageT t -> t + | PackStorageT _ -> NumT I32T + +let unpacked_field_type (FieldT (_mut, t)) = unpacked_storage_type t + +let as_def_heap_type (ht : heap_type) : def_type = + match ht with + | DefHT def -> def + | _ -> assert false + +let as_func_str_type (st : str_type) : func_type = + match st with + | DefFuncT ft -> ft + | _ -> assert false + +let as_cont_str_type (dt : str_type) : cont_type = + match dt with + | DefContT ct -> ct + | _ -> assert false + +let as_struct_str_type (st : str_type) : struct_type = + match st with + | DefStructT st -> st + | _ -> assert false + +let as_array_str_type (st : str_type) : array_type = + match st with + | DefArrayT at -> at + | _ -> assert false + +let as_cont_func_heap_type (ht : heap_type) : func_type = + let ContT ht' = as_cont_str_type (expand_def_type (as_def_heap_type ht)) in + as_func_str_type (expand_def_type (as_def_heap_type ht')) + +let as_cont_func_ref_type (rt : val_type) : func_type = + match rt with + | RefT (_, ht) -> as_cont_func_heap_type ht + | _ -> assert false + +let extern_type_of_import_type (ImportT (et, _, _)) = et +let extern_type_of_export_type (ExportT (et, _)) = et + (* String conversion *) let string_of_idx x = @@ -332,6 +368,9 @@ let string_of_num_type = function | F32T -> "f32" | F64T -> "f64" +let string_of_addr_type at = + string_of_num_type (num_type_of_addr_type at) + let string_of_vec_type = function | V128T -> "v128" @@ -348,6 +387,8 @@ let rec string_of_heap_type = function | NoExnHT -> "noexn" | ExternHT -> "extern" | NoExternHT -> "noextern" + | ContHT -> "cont" + | NoContHT -> "nocont" | VarHT x -> string_of_var x | DefHT dt -> "(" ^ string_of_def_type dt ^ ")" | BotHT -> "something" @@ -361,7 +402,6 @@ and string_of_val_type = function | RefT t -> string_of_ref_type t | BotT -> "bot" - and string_of_result_type = function | ts -> "[" ^ String.concat " " (List.map string_of_val_type ts) ^ "]" @@ -384,10 +424,18 @@ and string_of_func_type = function | FuncT (ts1, ts2) -> string_of_result_type ts1 ^ " -> " ^ string_of_result_type ts2 +and string_of_cont_type = function + | ContT ht -> string_of_heap_type ht + and string_of_str_type = function | DefStructT st -> "struct " ^ string_of_struct_type st | DefArrayT at -> "array " ^ string_of_array_type at | DefFuncT ft -> "func " ^ string_of_func_type ft + | DefContT ct -> "cont " ^ string_of_cont_type ct + + +and string_of_tag_type = function + | TagT dt -> string_of_def_type dt and string_of_sub_type = function | SubT (Final, [], st) -> string_of_str_type st @@ -406,24 +454,20 @@ and string_of_def_type = function | DefT (RecT [st], 0l) -> string_of_sub_type st | DefT (rt, i) -> "(" ^ string_of_rec_type rt ^ ")." ^ I32.to_string_u i - let string_of_limits = function | {min; max} -> - I32.to_string_u min ^ - (match max with None -> "" | Some n -> " " ^ I32.to_string_u n) + I64.to_string_u min ^ + (match max with None -> "" | Some n -> " " ^ I64.to_string_u n) let string_of_memory_type = function - | MemoryT lim -> string_of_limits lim + | MemoryT (at, lim) -> string_of_addr_type at ^ " " ^ string_of_limits lim let string_of_table_type = function - | TableT (lim, t) -> string_of_limits lim ^ " " ^ string_of_ref_type t + | TableT (at, lim, t) -> string_of_addr_type at ^ " " ^ string_of_limits lim ^ " " ^ string_of_ref_type t let string_of_global_type = function | GlobalT (mut, t) -> string_of_mut (string_of_val_type t) mut -let string_of_tag_type = function - | TagT dt -> string_of_def_type dt - let string_of_local_type = function | LocalT (Set, t) -> string_of_val_type t | LocalT (Unset, t) -> "(unset " ^ string_of_val_type t ^ ")" @@ -433,8 +477,7 @@ let string_of_extern_type = function | ExternTableT tt -> "table " ^ string_of_table_type tt | ExternMemoryT mt -> "memory " ^ string_of_memory_type mt | ExternGlobalT gt -> "global " ^ string_of_global_type gt - | ExternTagT tt -> "tag " ^ string_of_tag_type tt - + | ExternTagT t -> "tag " ^ string_of_tag_type t let string_of_export_type = function | ExportT (et, name) -> @@ -450,4 +493,4 @@ let string_of_module_type = function String.concat "" ( List.map (fun it -> "import " ^ string_of_import_type it ^ "\n") its @ List.map (fun et -> "export " ^ string_of_export_type et ^ "\n") ets - ) + ) diff --git a/interpreter/text/annot.ml b/interpreter/text/annot.ml new file mode 100644 index 000000000..e0fa73dfa --- /dev/null +++ b/interpreter/text/annot.ml @@ -0,0 +1,56 @@ +open Source + +type annot = annot' Source.phrase +and annot' = {name : Ast.name; items : item list} + +and item = item' Source.phrase +and item' = + | Atom of string + | Var of string + | String of string + | Nat of string + | Int of string + | Float of string + | Parens of item list + | Annot of annot + + +(* Stateful recorder for annotations *) +(* I wish this could be encapsulated in the parser somehow *) + +module NameMap = Map.Make(struct type t = Ast.name let compare = compare end) +type map = annot list NameMap.t + +let current : map ref = ref NameMap.empty +let current_source : Buffer.t = Buffer.create 512 + +let reset () = + current := NameMap.empty; + Buffer.clear current_source + +let get_source () = + Buffer.contents current_source + +let extend_source s = + Buffer.add_string current_source s + +let record annot = + let old = Option.value (NameMap.find_opt annot.it.name !current) ~default:[] in + current := NameMap.add annot.it.name (annot::old) !current + +let is_contained r1 r2 = r1.left >= r2.left && r1.right <= r2.right + +let get_all () = + let all = !current in + current := NameMap.empty; + all + +let filter f map = + NameMap.filter (fun _ annots -> annots <> []) + (NameMap.map (List.filter f) map) + +let get r = + let sub = filter (fun annot -> is_contained annot.at r) !current in + let map' = filter (fun annot -> not (is_contained annot.at r)) !current in + current := map'; + sub diff --git a/interpreter/text/annot.mli b/interpreter/text/annot.mli new file mode 100644 index 000000000..9cdfa0203 --- /dev/null +++ b/interpreter/text/annot.mli @@ -0,0 +1,25 @@ +type annot = annot' Source.phrase +and annot' = {name : Ast.name; items : item list} + +and item = item' Source.phrase +and item' = + | Atom of string + | Var of string + | String of string + | Nat of string + | Int of string + | Float of string + | Parens of item list + | Annot of annot + +module NameMap : Map.S with type key = Ast.name +type map = annot list NameMap.t + +val reset : unit -> unit +val record : annot -> unit + +val get : Source.region -> map +val get_all : unit -> map + +val get_source : unit -> string +val extend_source : string -> unit diff --git a/interpreter/text/arrange.ml b/interpreter/text/arrange.ml index 58f6bf63f..d369554cb 100644 --- a/interpreter/text/arrange.ml +++ b/interpreter/text/arrange.ml @@ -11,11 +11,14 @@ open Sexpr let nat n = I32.to_string_u (I32.of_int_u n) let nat32 = I32.to_string_u +let nat64 = I64.to_string_u let add_hex_char buf c = Printf.bprintf buf "\\%02x" (Char.code c) let add_char buf = function | '\n' -> Buffer.add_string buf "\\n" + | '\r' -> Buffer.add_string buf "\\r" | '\t' -> Buffer.add_string buf "\\t" + | '\'' -> Buffer.add_string buf "\\'" | '\"' -> Buffer.add_string buf "\\\"" | '\\' -> Buffer.add_string buf "\\\\" | c when '\x20' <= c && c < '\x7f' -> Buffer.add_char buf c @@ -61,6 +64,7 @@ let mutability node = function | Cons -> node | Var -> Node ("mut", [node]) +let addr_type t = string_of_addr_type t let num_type t = string_of_num_type t let vec_type t = string_of_vec_type t let ref_type t = @@ -96,11 +100,15 @@ let array_type (ArrayT ft) = let func_type (FuncT (ts1, ts2)) = Node ("func", decls "param" ts1 @ decls "result" ts2) +let cont_type (ContT ct) = + Node ("cont", [atom heap_type ct]) + let str_type st = match st with | DefStructT st -> struct_type st | DefArrayT at -> array_type at | DefFuncT ft -> func_type ft + | DefContT ct -> cont_type ct let sub_type = function | SubT (Final, [], st) -> str_type st @@ -255,6 +263,10 @@ struct | "32x4" -> "64x2" | _ -> assert false + let without_high_bit = function + | "8x16" -> "7x16" + | _ -> assert false + let voidop xxxx = function (_ : void) -> . let itestop xxxx (op : itestop) = match op with @@ -297,6 +309,13 @@ struct | NarrowU -> "narrow_i" ^ double xxxx ^ "_u" | Shuffle is -> "shuffle " ^ String.concat " " (List.map nat is) | Swizzle -> "swizzle" + | RelaxedSwizzle -> "relaxed_swizzle" + | RelaxedQ15MulRS -> "relaxed_q15mulr_s" + | RelaxedDot -> "relaxed_dot_i" ^ half xxxx ^ "_i" ^ without_high_bit (half xxxx) ^ "_s" + + let iternop xxxx (op : iternop) = match op with + | RelaxedLaneselect -> "relaxed_laneselect" + | RelaxedDotAccum -> "relaxed_dot_i" ^ half (half xxxx) ^ "_i" ^ without_high_bit (half (half xxxx)) ^ "_add_s" let fbinop xxxx (op : fbinop) = match op with | Add -> "add" @@ -307,6 +326,12 @@ struct | Max -> "max" | Pmin -> "pmin" | Pmax -> "pmax" + | RelaxedMin -> "relaxed_min" + | RelaxedMax -> "relaxed_max" + + let fternop xxxx (op : fternop) = match op with + | RelaxedMadd -> "relaxed_madd" + | RelaxedNmadd-> "relaxed_nmadd" let irelop xxxx (op : irelop) = match op with | Eq -> "eq" @@ -339,6 +364,10 @@ struct | TruncSatUF32x4 -> "trunc_sat_f32x4_u" | TruncSatSZeroF64x2 -> "trunc_sat_f64x2_s_zero" | TruncSatUZeroF64x2 -> "trunc_sat_f64x2_u_zero" + | RelaxedTruncSF32x4 -> "relaxed_trunc_f32x4_s" + | RelaxedTruncUF32x4 -> "relaxed_trunc_f32x4_u" + | RelaxedTruncSZeroF64x2 -> "relaxed_trunc_f64x2_s_zero" + | RelaxedTruncUZeroF64x2 -> "relaxed_trunc_f64x2_u_zero" let fcvtop xxxx (op : fcvtop) = match op with | DemoteZeroF64x2 -> "demote_f64x2_zero" @@ -418,6 +447,7 @@ let cvtop = oper (IntOp.cvtop, FloatOp.cvtop) let vec_unop = vec_shape_oper (V128Op.iunop, V128Op.iunop, V128Op.funop) let vec_binop = vec_shape_oper (V128Op.ibinop, V128Op.ibinop, V128Op.fbinop) +let vec_ternop = vec_shape_oper (V128Op.iternop, V128Op.iternop, V128Op.fternop) let vec_testop = vec_shape_oper (V128Op.itestop, V128Op.itestop, V128Op.voidop) let vec_relop = vec_shape_oper (V128Op.irelop, V128Op.irelop, V128Op.frelop) let vec_cvtop = vec_shape_oper (V128Op.icvtop, V128Op.icvtop, V128Op.fcvtop) @@ -431,15 +461,14 @@ let vec_splatop = vec_shape_oper (V128Op.splatop, V128Op.splatop, V128Op.splatop let vec_extractop = vec_shape_oper (V128Op.pextractop, V128Op.extractop, V128Op.extractop) let vec_replaceop = vec_shape_oper (V128Op.replaceop, V128Op.replaceop, V128Op.replaceop) - let var x = nat32 x.it let num v = string_of_num v.it let vec v = string_of_vec v.it let memop name x typ {ty; align; offset; _} sz = typ ty ^ "." ^ name ^ " " ^ var x ^ - (if offset = 0l then "" else " offset=" ^ nat32 offset) ^ - (if 1 lsl align = sz then "" else " align=" ^ nat (1 lsl align)) + (if offset = 0L then "" else " offset=" ^ nat64 offset) ^ + (if 1 lsl align = sz then "" else " align=" ^ nat64 (Int64.shift_left 1L align)) let loadop x op = match op.pack with @@ -483,6 +512,15 @@ let block_type = function | VarBlockType x -> [Node ("type " ^ var x, [])] | ValBlockType ts -> decls "result" (list_of_opt ts) +let hdl = function + | OnLabel x -> var x + | OnSwitch -> "switch" + +let resumetable xys = + List.map + (fun (x, y) -> Node ("on " ^ var x ^ " " ^ hdl y, [])) + xys + let rec instr e = let head, inner = match e.it with @@ -516,6 +554,17 @@ let rec instr e = | ReturnCallRef x -> "return_call_ref " ^ var x, [] | ReturnCallIndirect (x, y) -> "return_call_indirect " ^ var x, [Node ("type " ^ var y, [])] + | ContNew x -> "cont.new " ^ var x, [] + | ContBind (x, y) -> "cont.bind " ^ var x ^ " " ^ var y, [] + | Suspend x -> "suspend " ^ var x, [] + | Resume (x, xys) -> + "resume " ^ var x, resumetable xys + | ResumeThrow (x, y, xys) -> + "resume_throw " ^ var x ^ " " ^ var y, resumetable xys + | ResumeThrowRef (x, xys) -> + "resume_throw_ref " ^ var x, resumetable xys + | Switch (x, z) -> + "switch " ^ var x ^ " " ^ var z, [] | Throw x -> "throw " ^ var x, [] | ThrowRef -> "throw_ref", [] | TryTable (bt, cs, es) -> @@ -580,6 +629,7 @@ let rec instr e = | VecTest op -> vec_testop op, [] | VecUnary op -> vec_unop op, [] | VecBinary op -> vec_binop op, [] + | VecTernary op -> vec_ternop op, [] | VecCompare op -> vec_relop op, [] | VecConvert op -> vec_cvtop op, [] | VecShift op -> vec_shiftop op, [] @@ -623,17 +673,22 @@ let func f = func_with_name "" f -(* Tables & memories *) +(* Tags, tables, memories *) + +let tag off i tag = + Node ("tag $" ^ nat (off + i), + [Node ("type " ^ var (tag.it.tgtype), [])] + ) let table off i tab = - let {ttype = TableT (lim, t); tinit} = tab.it in - Node ("table $" ^ nat (off + i) ^ " " ^ limits nat32 lim, + let {ttype = TableT (at, lim, t); tinit} = tab.it in + Node ("table $" ^ nat (off + i) ^ " " ^ addr_type at ^ " " ^ limits nat64 lim, atom ref_type t :: list instr tinit.it ) let memory off i mem = - let {mtype = MemoryT lim} = mem.it in - Node ("memory $" ^ nat (off + i) ^ " " ^ limits nat32 lim, []) + let {mtype = MemoryT (at, lim)} = mem.it in + Node ("memory $" ^ nat (off + i) ^ " " ^ addr_type at ^ " " ^ limits nat64 lim, []) let is_elem_kind = function | (NoNull, FuncHT) -> true @@ -676,17 +731,6 @@ let data i seg = Node ("data $" ^ nat i, segment_mode "memory" dmode @ break_bytes dinit) -(* Tags *) - -let tag_with_name name (t : tag) = - Node ("tag" ^ name, - [Node ("type " ^ var (t.it.tgtype), [])] - ) - -let tag_with_index off i t = - tag_with_name (" $" ^ nat (off + i)) t - - (* Modules *) let type_ (ns, i) ty = @@ -720,8 +764,8 @@ let export_desc d = | FuncExport x -> Node ("func", [atom var x]) | TableExport x -> Node ("table", [atom var x]) | MemoryExport x -> Node ("memory", [atom var x]) - | TagExport x -> Node ("tag", [atom var x]) | GlobalExport x -> Node ("global", [atom var x]) + | TagExport x -> Node ("tag", [atom var x]) let export ex = let {name = n; edesc} = ex.it in @@ -734,41 +778,54 @@ let global off i g = let start s = Node ("start " ^ var s.it.sfunc, []) +let custom m mnode (module S : Custom.Section) = + S.Handler.arrange m mnode S.it -(* Modules *) + +let var x = + if String.for_all (fun c -> Lib.Char.is_alphanum_ascii c || c = '_') x.it then + "$" ^ x.it + else + "$" ^ name (Utf8.decode x.it) let var_opt = function | None -> "" - | Some x -> " " ^ x.it + | Some x -> " " ^ var x -let module_with_var_opt x_opt m = +let module_with_var_opt isdef x_opt (m, cs) = let fx = ref 0 in let tx = ref 0 in let mx = ref 0 in let tgx = ref 0 in let gx = ref 0 in let imports = list (import fx tx mx tgx gx) m.it.imports in - Node ("module" ^ var_opt x_opt, + let head = if isdef then "module definition" else "module" in + let ret = Node (head ^ var_opt x_opt, List.rev (fst (List.fold_left type_ ([], 0) m.it.types)) @ imports @ listi (table !tx) m.it.tables @ listi (memory !mx) m.it.memories @ - listi (tag_with_index !tgx) m.it.tags @ + listi (tag !tgx) m.it.tags @ listi (global !gx) m.it.globals @ - listi (func_with_index !fx) m.it.funcs @ list export m.it.exports @ opt start m.it.start @ listi elem m.it.elems @ + listi (func_with_index !fx) m.it.funcs @ listi data m.it.datas - ) + ) in + List.fold_left (custom m) ret cs -let binary_module_with_var_opt x_opt bs = - Node ("module" ^ var_opt x_opt ^ " binary", break_bytes bs) -let quoted_module_with_var_opt x_opt s = - Node ("module" ^ var_opt x_opt ^ " quote", break_string s) +let binary_module_with_var_opt isdef x_opt bs = + let head = if isdef then "module definition" else "module" in + Node (head ^ var_opt x_opt ^ " binary", break_bytes bs) -let module_ = module_with_var_opt None +let quoted_module_with_var_opt isdef x_opt s = + let head = if isdef then "module definition" else "module" in + Node (head ^ var_opt x_opt ^ " quote", break_string s) + +let module_with_custom = module_with_var_opt false None +let module_ m = module_with_custom (m, []) (* Scripts *) @@ -788,30 +845,33 @@ let literal mode lit = | Vec v -> Node (vec_constop v ^ " " ^ vec mode v, []) | Ref r -> ref_ r -let definition mode x_opt def = +let definition mode isdef x_opt def = try match mode with | `Textual -> let rec unquote def = match def.it with - | Textual m -> m - | Encoded (_, bs) -> Decode.decode "" bs - | Quoted (_, s) -> unquote (snd (Parse.Module.parse_string s)) - in module_with_var_opt x_opt (unquote def) + | Textual (m, cs) -> m, cs + | Encoded (name, bs) -> Decode.decode_with_custom name bs.it + | Quoted (_, s) -> + unquote (snd (Parse.Module.parse_string ~offset:s.at s.it)) + in module_with_var_opt isdef x_opt (unquote def) | `Binary -> let rec unquote def = match def.it with - | Textual m -> Encode.encode m - | Encoded (_, bs) -> Encode.encode (Decode.decode "" bs) - | Quoted (_, s) -> unquote (snd (Parse.Module.parse_string s)) - in binary_module_with_var_opt x_opt (unquote def) + | Textual (m, cs) -> Encode.encode_with_custom (m, cs) + | Encoded (name, bs) -> + Encode.encode_with_custom (Decode.decode_with_custom name bs.it) + | Quoted (_, s) -> + unquote (snd (Parse.Module.parse_string ~offset:s.at s.it)) + in binary_module_with_var_opt isdef x_opt (unquote def) | `Original -> match def.it with - | Textual m -> module_with_var_opt x_opt m - | Encoded (_, bs) -> binary_module_with_var_opt x_opt bs - | Quoted (_, s) -> quoted_module_with_var_opt x_opt s + | Textual (m, cs) -> module_with_var_opt isdef x_opt (m, cs) + | Encoded (_, bs) -> binary_module_with_var_opt isdef x_opt bs.it + | Quoted (_, s) -> quoted_module_with_var_opt isdef x_opt s.it with Parse.Syntax _ -> - quoted_module_with_var_opt x_opt "" + quoted_module_with_var_opt isdef x_opt "" let access x_opt n = String.concat " " [var_opt x_opt; name n] @@ -856,11 +916,15 @@ let ref_pat = function | RefTypePat t -> Node ("ref." ^ heap_type t, []) | NullPat -> Node ("ref.null", []) -let result mode res = +let rec result mode res = match res.it with | NumResult np -> num_pat mode np | VecResult vp -> vec_pat mode vp | RefResult rp -> ref_pat rp + | EitherResult ress -> Node ("either", List.map (result mode) ress) + +let instance (x1_opt, x2_opt) = + Node ("module instance" ^ var_opt x1_opt ^ var_opt x2_opt, []) let assertion mode ass = match ass.it with @@ -868,26 +932,37 @@ let assertion mode ass = (match mode, def.it with | `Binary, Quoted _ -> [] | _ -> - [Node ("assert_malformed", [definition `Original None def; Atom (string re)])] + [Node ("assert_malformed", [definition `Original false None def; Atom (string re)])] + ) + | AssertMalformedCustom (def, re) -> + (match mode, def.it with + | `Binary, Quoted _ -> [] + | _ -> + [Node ("assert_malformed_custom", [definition `Original false None def; Atom (string re)])] ) | AssertInvalid (def, re) -> - [Node ("assert_invalid", [definition mode None def; Atom (string re)])] - | AssertUnlinkable (def, re) -> - [Node ("assert_unlinkable", [definition mode None def; Atom (string re)])] - | AssertUninstantiable (def, re) -> - [Node ("assert_trap", [definition mode None def; Atom (string re)])] + [Node ("assert_invalid", [definition mode false None def; Atom (string re)])] + | AssertInvalidCustom (def, re) -> + [Node ("assert_invalid_custom", [definition mode false None def; Atom (string re)])] + | AssertUnlinkable (x_opt, re) -> + [Node ("assert_unlinkable", [instance (None, x_opt); Atom (string re)])] + | AssertUninstantiable (x_opt, re) -> + [Node ("assert_trap", [instance (None, x_opt); Atom (string re)])] | AssertReturn (act, results) -> [Node ("assert_return", action mode act :: List.map (result mode) results)] - | AssertTrap (act, re) -> - [Node ("assert_trap", [action mode act; Atom (string re)])] | AssertException act -> [Node ("assert_exception", [action mode act])] + | AssertTrap (act, re) -> + [Node ("assert_trap", [action mode act; Atom (string re)])] + | AssertSuspension (act, re) -> + [Node ("assert_suspension", [action mode act; Atom (string re)])] | AssertExhaustion (act, re) -> [Node ("assert_exhaustion", [action mode act; Atom (string re)])] let command mode cmd = match cmd.it with - | Module (x_opt, def) -> [definition mode x_opt def] + | Module (x_opt, def) -> [definition mode true x_opt def] + | Instance (x1_opt, x2_opt) -> [instance (x1_opt, x2_opt)] | Register (n, x_opt) -> [Node ("register " ^ name n ^ var_opt x_opt, [])] | Action act -> [action mode act] | Assertion ass -> assertion mode ass diff --git a/interpreter/text/arrange.mli b/interpreter/text/arrange.mli index 051686a44..a0fddd5d4 100644 --- a/interpreter/text/arrange.mli +++ b/interpreter/text/arrange.mli @@ -1,6 +1,14 @@ open Sexpr +val bytes : string -> string +val string : string -> string +val name : Ast.name -> string + +val break_bytes : string -> sexpr list +val break_string : string -> sexpr list + val instr : Ast.instr -> sexpr val func : Ast.func -> sexpr val module_ : Ast.module_ -> sexpr +val module_with_custom : Ast.module_ * Custom.section list -> sexpr val script : [`Textual | `Binary] -> Script.script -> sexpr list diff --git a/interpreter/text/lexer.mll b/interpreter/text/lexer.mll index 3bd457ffd..001b42946 100644 --- a/interpreter/text/lexer.mll +++ b/interpreter/text/lexer.mll @@ -48,6 +48,11 @@ let string s = done; Buffer.contents b +let annot_id lexbuf s = + let s' = string s in + if s' = "" then error lexbuf "empty annotation id"; + try Utf8.decode s' with Utf8.Utf8 -> error lexbuf "malformed UTF-8 encoding" + let opt = Lib.Option.get } @@ -102,8 +107,7 @@ let float = let string = '"' character* '"' let idchar = letter | digit | '_' | symbol -let name = idchar+ -let id = '$' name +let id = idchar+ let keyword = ['a'-'z'] (letter | digit | '_' | '.' | ':')+ let reserved = (idchar | string)+ | ',' | ';' | '[' | ']' | '{' | '}' @@ -173,6 +177,9 @@ rule token = parse | "noextern" -> NOEXTERN | "externref" -> EXTERNREF | "nullexternref" -> NULLEXTERNREF + | "nocont" -> NOCONT + | "contref" -> CONTREF + | "nullcontref" -> NULLCONTREF | "ref" -> REF | "null" -> NULL @@ -180,6 +187,7 @@ rule token = parse | "struct" -> STRUCT | "field" -> FIELD | "mut" -> MUT + | "cont" -> CONT | "sub" -> SUB | "final" -> FINAL | "rec" -> REC @@ -216,6 +224,16 @@ rule token = parse | "catch_all" -> CATCH_ALL | "catch_all_ref" -> CATCH_ALL_REF + + | "cont.new" -> CONT_NEW + | "cont.bind" -> CONT_BIND + | "suspend" -> SUSPEND + | "resume" -> RESUME + | "resume_throw" -> RESUME_THROW + | "resume_throw_ref" -> RESUME_THROW_REF + | "switch" -> SWITCH + + | "local.get" -> LOCAL_GET | "local.set" -> LOCAL_SET | "local.tee" -> LOCAL_TEE @@ -738,6 +756,27 @@ rule token = parse | "f32x4.replace_lane" -> VEC_REPLACE f32x4_replace_lane | "f64x2.replace_lane" -> VEC_REPLACE f64x2_replace_lane + | "i8x16.relaxed_swizzle" -> VEC_BINARY i8x16_relaxed_swizzle + | "i32x4.relaxed_trunc_f32x4_u" -> VEC_UNARY i32x4_relaxed_trunc_f32x4_u + | "i32x4.relaxed_trunc_f32x4_s" -> VEC_UNARY i32x4_relaxed_trunc_f32x4_s + | "i32x4.relaxed_trunc_f64x2_u_zero" -> VEC_UNARY i32x4_relaxed_trunc_f64x2_u_zero + | "i32x4.relaxed_trunc_f64x2_s_zero" -> VEC_UNARY i32x4_relaxed_trunc_f64x2_s_zero + | "f32x4.relaxed_madd" -> VEC_UNARY f32x4_relaxed_madd + | "f64x2.relaxed_madd" -> VEC_UNARY f64x2_relaxed_madd + | "f32x4.relaxed_nmadd" -> VEC_UNARY f32x4_relaxed_nmadd + | "f64x2.relaxed_nmadd" -> VEC_UNARY f64x2_relaxed_nmadd + | "i8x16.relaxed_laneselect" -> VEC_TERNARY i8x16_relaxed_laneselect + | "i16x8.relaxed_laneselect" -> VEC_TERNARY i16x8_relaxed_laneselect + | "i32x4.relaxed_laneselect" -> VEC_TERNARY i32x4_relaxed_laneselect + | "i64x2.relaxed_laneselect" -> VEC_TERNARY i64x2_relaxed_laneselect + | "f32x4.relaxed_min" -> VEC_UNARY f32x4_relaxed_min + | "f64x2.relaxed_min" -> VEC_UNARY f64x2_relaxed_min + | "f32x4.relaxed_max" -> VEC_UNARY f32x4_relaxed_max + | "f64x2.relaxed_max" -> VEC_UNARY f64x2_relaxed_max + | "i16x8.relaxed_q15mulr_s" -> VEC_BINARY i16x8_relaxed_q15mulr_s + | "i16x8.relaxed_dot_i8x16_i7x16_s" -> VEC_BINARY i16x8_relaxed_dot_i8x16_i7x16_s + | "i32x4.relaxed_dot_i8x16_i7x16_add_s" -> VEC_BINARY i32x4_relaxed_dot_i8x16_i7x16_add_s + | "type" -> TYPE | "func" -> FUNC | "param" -> PARAM @@ -752,6 +791,7 @@ rule token = parse | "data" -> DATA | "declare" -> DECLARE | "offset" -> OFFSET + | "on" -> ON | "item" -> ITEM | "import" -> IMPORT | "export" -> EXPORT @@ -759,6 +799,8 @@ rule token = parse | "module" -> MODULE | "binary" -> BIN | "quote" -> QUOTE + | "definition" -> DEFINITION + | "instance" -> INSTANCE | "script" -> SCRIPT | "register" -> REGISTER @@ -766,13 +808,17 @@ rule token = parse | "get" -> GET | "assert_malformed" -> ASSERT_MALFORMED | "assert_invalid" -> ASSERT_INVALID + | "assert_malformed_custom" -> ASSERT_MALFORMED_CUSTOM + | "assert_invalid_custom" -> ASSERT_INVALID_CUSTOM | "assert_unlinkable" -> ASSERT_UNLINKABLE | "assert_return" -> ASSERT_RETURN | "assert_trap" -> ASSERT_TRAP | "assert_exception" -> ASSERT_EXCEPTION | "assert_exhaustion" -> ASSERT_EXHAUSTION + | "assert_suspension" -> ASSERT_SUSPENSION | "nan:canonical" -> NAN Script.CanonicalNan | "nan:arithmetic" -> NAN Script.ArithmeticNan + | "either" -> EITHER | "input" -> INPUT | "output" -> OUTPUT @@ -782,7 +828,21 @@ rule token = parse | "offset="(nat as s) { OFFSET_EQ_NAT s } | "align="(nat as s) { ALIGN_EQ_NAT s } - | id as s { VAR s } + | '$'(id as s) { VAR s } + | '$'(string as s) + { let s' = string s in + if s' = "" then error lexbuf "empty identifier"; VAR s' } + | '$' { error lexbuf "empty identifier" } + + | "(@"(id as n) + { let r = region lexbuf in + let items = annot (Lexing.lexeme_start_p lexbuf) lexbuf in + Annot.record (Annot.{name = Utf8.decode n; items} @@ r); token lexbuf } + | "(@"(string as s) + { let r = region lexbuf in + let items = annot (Lexing.lexeme_start_p lexbuf) lexbuf in + Annot.record (Annot.{name = annot_id lexbuf s; items} @@ r); token lexbuf } + | "(@" { error lexbuf "empty annotation id" } | ";;"utf8_no_nl*eof { EOF } | ";;"utf8_no_nl*newline { Lexing.new_line lexbuf; token lexbuf } @@ -797,6 +857,64 @@ rule token = parse | utf8enc { error lexbuf "misplaced unicode character" } | _ { error lexbuf "malformed UTF-8 encoding" } +and annot start = parse + | ")" { [] } + | "(" + { let r = region lexbuf in + let items = annot (Lexing.lexeme_start_p lexbuf) lexbuf in + (Annot.Parens items @@ r) :: annot start lexbuf } + | "(@"(id as n) + { let r = region lexbuf in + let items = annot (Lexing.lexeme_start_p lexbuf) lexbuf in + let ann = Annot.{name = Utf8.decode n; items} @@ r in + (Annot.Annot ann @@ r) :: annot start lexbuf } + | "(@"(string as s) + { let r = region lexbuf in + let items = annot (Lexing.lexeme_start_p lexbuf) lexbuf in + let ann = Annot.{name = annot_id lexbuf s; items} @@ r in + (Annot.Annot ann @@ r) :: annot start lexbuf } + + | nat as s + { let r = region lexbuf in + (Annot.Nat s @@ r) :: annot start lexbuf } + | int as s + { let r = region lexbuf in + (Annot.Int s @@ r) :: annot start lexbuf } + | float as s + { let r = region lexbuf in + (Annot.Float s @@ r) :: annot start lexbuf } + | '$'(id as s) + { let r = region lexbuf in + (Annot.Var s @@ r) :: annot start lexbuf } + | '$'(string as s) + { let r = region lexbuf in + let s' = string s in + if s' = "" then error lexbuf "empty identifier"; + (Annot.Var s' @@ r) :: annot start lexbuf } + | '$' { error lexbuf "empty identifier" } + | string as s + { let r = region lexbuf in + (Annot.String (string s) @@ r) :: annot start lexbuf } + | reserved as s + { let r = region lexbuf in + (Annot.Atom s @@ r) :: annot start lexbuf } + | '"'character*('\n'|eof) + { error lexbuf "unclosed string literal" } + | '"'character*['\x00'-'\x09''\x0b'-'\x1f''\x7f'] + { error lexbuf "illegal control character in string literal" } + | '"'character*'\\'_ + { error_nest (Lexing.lexeme_end_p lexbuf) lexbuf "illegal escape" } + + | (";;"utf8_no_nl*)? eof { error_nest start lexbuf "unclosed annotation" } + | ";;"utf8_no_nl*'\n' { Lexing.new_line lexbuf; annot start lexbuf } + | ";;"utf8_no_nl* { annot start lexbuf (* error on following position *) } + | "(;" { comment (Lexing.lexeme_start_p lexbuf) lexbuf; annot start lexbuf } + | space#'\n' { annot start lexbuf } + | '\n' { Lexing.new_line lexbuf; annot start lexbuf } + | eof { error_nest start lexbuf "unclosed annotation" } + | utf8 { error lexbuf "illegal character" } + | _ { error lexbuf "malformed UTF-8 encoding" } + and comment start = parse | ";)" { () } | "(;" { comment (Lexing.lexeme_start_p lexbuf) lexbuf; comment start lexbuf } diff --git a/interpreter/text/parse.ml b/interpreter/text/parse.ml index 94e5d5549..986f44ddd 100644 --- a/interpreter/text/parse.ml +++ b/interpreter/text/parse.ml @@ -5,10 +5,25 @@ sig type t val parse : string -> Lexing.lexbuf -> t val parse_file : string -> t - val parse_string : string -> t + val parse_string : ?offset:Source.region -> string -> t val parse_channel : in_channel -> t end +let wrap_lexbuf lexbuf = + let open Lexing in + let inner_refill = lexbuf.refill_buff in + let refill_buff lexbuf = + let oldlen = lexbuf.lex_buffer_len - lexbuf.lex_start_pos in + inner_refill lexbuf; + let newlen = lexbuf.lex_buffer_len - lexbuf.lex_start_pos in + let start = lexbuf.lex_start_pos + oldlen in + let n = newlen - oldlen in + Annot.extend_source (Bytes.sub_string lexbuf.lex_buffer start n) + in + let n = lexbuf.lex_buffer_len - lexbuf.lex_start_pos in + Annot.extend_source (Bytes.sub_string lexbuf.lex_buffer lexbuf.lex_start_pos n); + {lexbuf with refill_buff} + let convert_pos lexbuf = { Source.left = Lexer.convert_pos lexbuf.Lexing.lex_start_p; Source.right = Lexer.convert_pos lexbuf.Lexing.lex_curr_p @@ -19,12 +34,33 @@ let make (type a) (start : _ -> _ -> a) : (module S with type t = a) = type t = a let parse name lexbuf = + Annot.reset (); Lexing.set_filename lexbuf name; - try start Lexer.token lexbuf with Parser.Error -> - raise (Syntax (convert_pos lexbuf, "unexpected token")) + let lexbuf = wrap_lexbuf lexbuf in + let result = + try start Lexer.token lexbuf with Parser.Error -> + raise (Syntax (convert_pos lexbuf, "unexpected token")) + in + let annots = Annot.get_all () in + if not (Annot.NameMap.is_empty annots) then + let annot = List.hd (snd (Annot.NameMap.choose annots)) in + raise (Custom.Syntax (annot.Source.at, "misplaced annotation")) + else + result - let parse_string s = - parse "string" (Lexing.from_string ~with_positions:true s) + let parse_string ?offset s = + let open Source in + let name, s' = + match offset with + | None -> "string", s + | Some at -> + (* Note: this is a hack that only works for singular string literals + * with no escapes in them. + * TODO: Figure out why we need to add 2 instead of 1 to column. *) + at.left.file, + String.make (max 0 (at.left.line - 1)) '\n' ^ + String.make (at.left.column + 2) ' ' ^ s + in parse name (Lexing.from_string ~with_positions:true s') let parse_channel oc = parse "channel" (Lexing.from_channel ~with_positions:true oc) diff --git a/interpreter/text/parse.mli b/interpreter/text/parse.mli index 3a318683b..f5a411694 100644 --- a/interpreter/text/parse.mli +++ b/interpreter/text/parse.mli @@ -5,7 +5,7 @@ sig type t val parse : string -> Lexing.lexbuf -> t (* raises Syntax *) val parse_file : string -> t (* raises Syntax *) - val parse_string : string -> t (* raises Syntax *) + val parse_string : ?offset:Source.region -> string -> t (* raises Syntax *) val parse_channel : in_channel -> t (* raises Syntax *) end diff --git a/interpreter/text/parser.mly b/interpreter/text/parser.mly index 966eaedf5..617b299b5 100644 --- a/interpreter/text/parser.mly +++ b/interpreter/text/parser.mly @@ -10,6 +10,8 @@ open Script let error at msg = raise (Parse_error.Syntax (at, msg)) +let thd (_, _, x) = x + (* Position handling *) @@ -75,18 +77,20 @@ let nanop f nan = | F64 _ -> F64 nan.it @@ nan.at | I32 _ | I64 _ -> error nan.at "NaN pattern with non-float type" -let nat s loc = - try - let n = int_of_string s in - if n >= 0 then n else raise (Failure "") - with Failure _ -> error (at loc) "integer constant out of range" - let nat32 s loc = try I32.of_string_u s with Failure _ -> error (at loc) "i32 constant out of range" +let nat64 s loc = + try I64.of_string_u s with Failure _ -> error (at loc) "i64 constant out of range" + let name s loc = try Utf8.decode s with Utf8.Utf8 -> error (at loc) "malformed UTF-8 encoding" +let var s loc = + let r = at loc in + try ignore (Utf8.decode s); Source.(s @@ r) + with Utf8.Utf8 -> error r "malformed UTF-8 encoding" + (* Symbolic variables *) @@ -144,9 +148,24 @@ let force_locals (c : context) = c.deferred_locals := [] +let print_char = function + | 0x09 -> "\\t" + | 0x0a -> "\\n" + | 0x22 -> "\\\"" + | 0x5c -> "\\\\" + | c when 0x20 <= c && c < 0x7f -> String.make 1 (Char.chr c) + | c -> Printf.sprintf "\\u{%02x}" c + +let print x = + "$" ^ + if String.for_all (fun c -> Lib.Char.is_alphanum_ascii c || c = '_') x.it + then x.it + else "\"" ^ String.concat "" (List.map print_char (Utf8.decode x.it)) ^ "\"" + + let lookup category space x = try VarMap.find x.it space.map - with Not_found -> error x.at ("unknown " ^ category ^ " " ^ x.it) + with Not_found -> error x.at ("unknown " ^ category ^ " " ^ print x) let type_ (c : context) x = lookup "type" c.types.space x let func (c : context) x = lookup "function" c.funcs x @@ -167,10 +186,9 @@ let func_type (c : context) x = | _ -> error x.at ("non-function type " ^ Int32.to_string x.it) | exception Failure _ -> error x.at ("unknown type " ^ Int32.to_string x.it) - let bind_abs category space x = if VarMap.mem x.it space.map then - error x.at ("duplicate " ^ category ^ " " ^ x.it); + error x.at ("duplicate " ^ category ^ " " ^ print x); let i = bind category space 1l x.at in space.map <- VarMap.add x.it i space.map; i @@ -217,9 +235,8 @@ let anon_label (c : context) loc = bind "label" c.labels 1l (at loc) let anon_fields (c : context) x n loc = bind "field" (Lib.List32.nth c.types.fields x) n (at loc) - -let inline_func_type (c : context) ft loc = - let st = SubT (Final, [], DefFuncT ft) in +let find_type_index (c : context) dt loc = + let st = SubT (Final, [], dt) in match Lib.List.index_where (function | DefT (RecT [st'], 0l) -> st = st' @@ -233,6 +250,10 @@ let inline_func_type (c : context) ft loc = define_def_type c (DefT (RecT [st], 0l)); i @@ loc +let inline_func_type (c : context) ft loc = + let dt = DefFuncT ft in + find_type_index c dt loc + let inline_func_type_explicit (c : context) x ft loc = if ft = FuncT ([], []) then (* Deferring ensures that type lookup is only triggered when @@ -245,6 +266,29 @@ let inline_func_type_explicit (c : context) x ft loc = error (at loc) "inline function type does not match explicit type"; x +(* Custom annotations *) + +let parse_annots (m : module_) : Custom.section list = + let bs = Annot.get_source () in + let annots = Annot.get m.at in + let secs = + Annot.NameMap.fold (fun name anns secs -> + match Custom.handler name with + | Some (module Handler) -> + let secs' = Handler.parse m bs anns in + List.map (fun fmt -> + let module S = struct module Handler = Handler let it = fmt end in + (module S : Custom.Section) + ) secs' @ secs + | None -> + if !Flags.custom_reject then + raise (Custom.Syntax ((List.hd anns).at, + "unknown annotation @" ^ Utf8.encode name)) + else [] + ) annots [] + in + List.stable_sort Custom.compare_section secs + %} %token LPAR RPAR @@ -255,11 +299,13 @@ let inline_func_type_explicit (c : context) x ft loc = %token VEC_SHAPE %token ANYREF NULLREF EQREF I31REF STRUCTREF ARRAYREF %token FUNCREF NULLFUNCREF EXNREF NULLEXNREF EXTERNREF NULLEXTERNREF +%token NOCONT CONTREF NULLCONTREF %token ANY NONE EQ I31 REF NOFUNC EXN NOEXN EXTERN NOEXTERN NULL %token MUT FIELD STRUCT ARRAY SUB FINAL REC %token UNREACHABLE NOP DROP SELECT %token BLOCK END IF THEN ELSE LOOP -%token BR BR_IF BR_TABLE +%token CONT_NEW CONT_BIND SUSPEND RESUME RESUME_THROW RESUME_THROW_REF SWITCH +%token BR BR_IF BR_TABLE BR_ON_NON_NULL %token Ast.instr'> BR_ON_NULL %token Types.ref_type -> Types.ref_type -> Ast.instr'> BR_ON_CAST %token CALL CALL_REF CALL_INDIRECT @@ -290,13 +336,15 @@ let inline_func_type_explicit (c : context) x ft loc = %token VEC_SHIFT VEC_BITMASK VEC_SPLAT %token VEC_SHUFFLE %token Ast.instr'> VEC_EXTRACT VEC_REPLACE -%token FUNC START TYPE PARAM RESULT LOCAL GLOBAL -%token TABLE ELEM MEMORY TAG DATA DECLARE OFFSET ITEM IMPORT EXPORT -%token MODULE BIN QUOTE +%token FUNC START TYPE PARAM RESULT LOCAL GLOBAL CONT +%token TABLE ELEM MEMORY ON TAG DATA DECLARE OFFSET ITEM IMPORT EXPORT +%token MODULE BIN QUOTE DEFINITION INSTANCE %token SCRIPT REGISTER INVOKE GET %token ASSERT_MALFORMED ASSERT_INVALID ASSERT_UNLINKABLE -%token ASSERT_RETURN ASSERT_TRAP ASSERT_EXCEPTION ASSERT_EXHAUSTION +%token ASSERT_RETURN ASSERT_TRAP ASSERT_EXCEPTION ASSERT_EXHAUSTION ASSERT_SUSPENSION +%token ASSERT_MALFORMED_CUSTOM ASSERT_INVALID_CUSTOM %token NAN +%token EITHER %token INPUT OUTPUT %token EOF @@ -319,6 +367,14 @@ string_list : /* Types */ +%inline addr_type : + | NUM_TYPE + { match $1 with + | I32T -> I32AT + | I64T -> I64AT + | _ -> error (at $sloc) "malformed address type" } + | /* empty */ { I32AT } /* Sugar */ + null_opt : | /* empty */ { NoNull } | NULL { Null } @@ -336,6 +392,8 @@ heap_type : | NOEXN { fun c -> NoExnHT } | EXTERN { fun c -> ExternHT } | NOEXTERN { fun c -> NoExternHT } + | CONT { fun c -> ContHT } + | NOCONT { fun c -> NoContHT } | var { fun c -> VarHT (StatX ($1 c type_).it) } ref_type : @@ -352,6 +410,8 @@ ref_type : | NULLEXNREF { fun c -> (Null, NoExnHT) } /* Sugar */ | EXTERNREF { fun c -> (Null, ExternHT) } /* Sugar */ | NULLEXTERNREF { fun c -> (Null, NoExternHT) } /* Sugar */ + | CONTREF { fun c -> (Null, ContHT) } /* Sugar */ + | NULLCONTREF { fun c -> (Null, NoContHT) } /* Sugar */ val_type : | NUM_TYPE { fun c -> NumT $1 } @@ -366,6 +426,34 @@ global_type : | val_type { fun c -> GlobalT (Cons, $1 c) } | LPAR MUT val_type RPAR { fun c -> GlobalT (Var, $3 c) } +cont_type : + | type_use cont_type_params + { let at1 = $loc($1) in + fun c -> + match $2 c with + | FuncT ([], []) -> ContT (VarHT (StatX ($1 c).it)) + | ft -> + let x = inline_func_type_explicit c ($1 c) ft at1 in + ContT (VarHT (StatX x.it)) } + | cont_type_params + /* TODO: the inline type is broken for now */ + { let at = $sloc in fun c -> ContT (VarHT (StatX (inline_func_type c ($1 c) at).it)) } + | var /* Sugar */ + { fun c -> ContT (VarHT (StatX ($1 c type_).it)) } + +cont_type_params : + | LPAR PARAM val_type_list RPAR cont_type_params + { fun c -> let FuncT (ts1, ts2) = $5 c in + FuncT (snd $3 c @ ts1, ts2) } + | cont_type_results + { fun c -> FuncT ([], $1 c) } + +cont_type_results : + | LPAR RESULT val_type_list RPAR cont_type_results + { fun c -> snd $3 c @ $5 c } + | /* empty */ + { fun c -> [] } + storage_type : | val_type { fun c -> ValStorageT ($1 c) } | PACK_TYPE { fun c -> PackStorageT $1 } @@ -412,6 +500,7 @@ str_type : | LPAR STRUCT struct_type RPAR { fun c x -> DefStructT ($3 c x) } | LPAR ARRAY array_type RPAR { fun c x -> DefArrayT ($3 c) } | LPAR FUNC func_type RPAR { fun c x -> DefFuncT ($3 c) } + | LPAR CONT cont_type RPAR { fun c x -> DefContT ($3 c) } sub_type : | str_type { fun c x -> SubT (Final, [], $1 c x) } @@ -423,14 +512,14 @@ sub_type : List.map (fun y -> VarHT (StatX y.it)) ($4 c type_), $5 c x) } table_type : - | limits ref_type { fun c -> TableT ($1, $2 c) } + | addr_type limits ref_type { fun c -> TableT ($1, $2, $3 c) } memory_type : - | limits { fun c -> MemoryT $1 } + | addr_type limits { fun c -> MemoryT ($1, $2) } limits : - | NAT { {min = nat32 $1 $loc($1); max = None} } - | NAT NAT { {min = nat32 $1 $loc($1); max = Some (nat32 $2 $loc($2))} } + | NAT { {min = nat64 $1 $loc($1); max = None} } + | NAT NAT { {min = nat64 $1 $loc($1); max = Some (nat64 $2 $loc($2))} } type_use : | LPAR TYPE var RPAR { fun c -> $3 c type_ } @@ -448,7 +537,7 @@ num : var : | NAT { fun c lookup -> nat32 $1 $sloc @@ $sloc } - | VAR { fun c lookup -> lookup c ($1 @@ $sloc) @@ $sloc } + | VAR { fun c lookup -> lookup c (var $1 $sloc) @@ $sloc } var_opt : | /* empty */ { fun c lookup at -> 0l @@ at } @@ -467,7 +556,7 @@ bind_var_opt : | bind_var { fun c anon bind -> bind c $1 } /* Sugar */ bind_var : - | VAR { $1 @@ $sloc } + | VAR { var $1 $sloc } labeling_opt : | /* empty */ @@ -485,18 +574,18 @@ labeling_end_opt : | bind_var { [$1] } offset_ : - | OFFSET_EQ_NAT { nat32 $1 $sloc } + | OFFSET_EQ_NAT { nat64 $1 $sloc } offset_opt : - | /* empty */ { 0l } + | /* empty */ { 0L } | offset_ { $1 } align : | ALIGN_EQ_NAT - { let n = nat $1 $sloc in - if not (Lib.Int.is_power_of_two n) then + { let n = nat64 $1 $sloc in + if not (Lib.Int64.is_power_of_two_unsigned n) then error (at $sloc) "alignment must be a power of two"; - Some (Lib.Int.log2 n) } + Some (Int64.to_int (Lib.Int64.log2_unsigned n)) } align_opt : | /* empty */ { None } @@ -510,6 +599,7 @@ instr_list : | instr1 instr_list { fun c -> $1 c @ $2 c } | select_instr_instr_list { $1 } | call_instr_instr_list { $1 } + | resume_instr_instr_list { $1 } instr1 : | plain_instr { fun c -> [$1 c @@ $sloc] } @@ -526,12 +616,16 @@ plain_instr : { fun c -> let xs, x = Lib.List.split_last ($2 c label :: $3 c label) in br_table xs x } | BR_ON_NULL var { fun c -> $1 ($2 c label) } + | BR_ON_NON_NULL var { fun c -> br_on_non_null ($2 c label) } | BR_ON_CAST var ref_type ref_type { fun c -> $1 ($2 c label) ($3 c) ($4 c) } | RETURN { fun c -> return } | CALL var { fun c -> call ($2 c func) } | CALL_REF var { fun c -> call_ref ($2 c type_) } | RETURN_CALL var { fun c -> return_call ($2 c func) } | RETURN_CALL_REF var { fun c -> return_call_ref ($2 c type_) } + | CONT_NEW var { fun c -> cont_new ($2 c type_) } + | CONT_BIND var var { fun c -> cont_bind ($2 c type_) ($3 c type_) } + | SUSPEND var { fun c -> suspend ($2 c tag) } | THROW var { fun c -> throw ($2 c tag) } | THROW_REF { fun c -> throw_ref } | LOCAL_GET var { fun c -> local_get ($2 c local) } @@ -583,6 +677,7 @@ plain_instr : | STRUCT_NEW var { fun c -> $1 ($2 c type_) } | STRUCT_GET var var { fun c -> let x = $2 c type_ in $1 x ($3 c (field x.it)) } | STRUCT_SET var var { fun c -> let x = $2 c type_ in struct_set x ($3 c (field x.it)) } + | SWITCH var var { fun c -> let x = $2 c type_ in let tag = $3 c tag in switch x tag } | ARRAY_NEW var { fun c -> $1 ($2 c type_) } | ARRAY_NEW_FIXED var nat32 { fun c -> array_new_fixed ($2 c type_) $3 } | ARRAY_NEW_ELEM var var { fun c -> array_new_elem ($2 c type_) ($3 c elem) } @@ -627,10 +722,10 @@ lane_imms : { fun instr at0 c -> instr (0l @@ at0) $2 $1 (vec_lane_index $3 (at $loc($3))) } | align NAT /* Sugar */ - { fun instr at0 c -> instr (0l @@ at0) $1 0l + { fun instr at0 c -> instr (0l @@ at0) $1 0L (vec_lane_index $2 (at $loc($2))) } | NAT /* Sugar */ - { fun instr at0 c -> instr (0l @@ at0) None 0l + { fun instr at0 c -> instr (0l @@ at0) None 0L (vec_lane_index $1 (at $loc($1))) } @@ -682,6 +777,31 @@ call_instr_results_instr_list : | instr_list { fun c -> [], $1 c } +resume_instr_instr_list : + | RESUME var resume_instr_handler_instr + { let loc1 = $loc($1) in + fun c -> + let x = $2 c type_ in + let hs, es = $3 c in (resume x hs @@ loc1) :: es } + | RESUME_THROW var var resume_instr_handler_instr + { let loc1 = $loc($1) in + fun c -> + let x = $2 c type_ in + let tag = $3 c tag in + let hs, es = $4 c in (resume_throw x tag hs @@ loc1) :: es } + | RESUME_THROW_REF var resume_instr_handler_instr + { let loc1 = $loc($1) in + fun c -> + let x = $2 c type_ in + let hs, es = $3 c in (resume_throw_ref x hs @@ loc1) :: es } + +resume_instr_handler_instr : + | LPAR ON var var RPAR resume_instr_handler_instr + { fun c -> let hs, es = $6 c in ($3 c tag, OnLabel ($4 c label)) :: hs, es } + | LPAR ON var SWITCH RPAR resume_instr_handler_instr + { fun c -> let hs, es = $6 c in ($3 c tag, OnSwitch) :: hs, es } + | instr_list + { fun c -> [], $1 c } block_instr : | BLOCK labeling_opt block END labeling_end_opt @@ -782,6 +902,21 @@ expr1 : /* Sugar */ { fun c -> let x, es = $3 c in es, return_call_indirect ($2 c table) x } | RETURN_CALL_INDIRECT call_expr_type /* Sugar */ { fun c -> let x, es = $2 c in es, return_call_indirect (0l @@ $loc($1)) x } + | RESUME var resume_expr_handler + { fun c -> + let x = $2 c type_ in + let hs, es = $3 c in es, resume x hs } + | RESUME_THROW var var resume_expr_handler + { fun c -> + let x = $2 c type_ in + let tag = $3 c tag in + let hs, es = $4 c in + es, resume_throw x tag hs } + | RESUME_THROW_REF var resume_expr_handler + { fun c -> + let x = $2 c type_ in + let hs, es = $3 c in + es, resume_throw_ref x hs } | BLOCK labeling_opt block { fun c -> let c' = $2 c [] in let bt, es = $3 c' in [], block bt es } | LOOP labeling_opt block @@ -821,6 +956,13 @@ call_expr_results : | expr_list { fun c -> [], $1 c } +resume_expr_handler : + | LPAR ON var var RPAR resume_expr_handler + { fun c -> let hs, es = $6 c in ($3 c tag, OnLabel ($4 c label)) :: hs, es } + | LPAR ON var SWITCH RPAR resume_expr_handler + { fun c -> let hs, es = $6 c in ($3 c tag, OnSwitch) :: hs, es } + | expr_list + { fun c -> [], $1 c } if_block : | type_use if_block_param_body @@ -830,7 +972,7 @@ if_block : | if_block_param_body /* Sugar */ { fun c c' -> let ft, es = $1 c c' in let bt = - match ft with + match fst ($1 c c') with | FuncT ([], []) -> ValBlockType None | FuncT ([], [t]) -> ValBlockType (Some t) | ft -> VarBlockType (inline_func_type c ft $sloc) @@ -857,7 +999,6 @@ if_ : | LPAR THEN instr_list RPAR /* Sugar */ { fun c c' -> [], $3 c', [] } - try_block : | type_use try_block_param_body { fun c c' -> @@ -925,7 +1066,7 @@ func_fields : | type_use func_fields_body { fun c x loc -> let c' = enter_func c loc in - let y = inline_func_type_explicit c' ($1 c') (fst $2 c') loc in + let y = inline_func_type_explicit c' ($1 c') (fst $2 c') $loc($1) in [{(snd $2 c') with ftype = y} @@ loc], [], [] } | func_fields_body /* Sugar */ { fun c x loc -> @@ -934,7 +1075,7 @@ func_fields : [{(snd $1 c') with ftype = y} @@ loc], [], [] } | inline_import type_use func_fields_import /* Sugar */ { fun c x loc -> - let y = inline_func_type_explicit c ($2 c) ($3 c) loc in + let y = inline_func_type_explicit c ($2 c) ($3 c) $loc($2) in [], [{ module_name = fst $1; item_name = snd $1; idesc = FuncImport y @@ loc } @@ loc ], [] } @@ -993,11 +1134,11 @@ local_type : | val_type { fun c -> {ltype = $1 c} @@ $sloc } local_type_list : - | list(local_type) - { Lib.List32.length $1, fun c -> List.map (fun f -> f c) $1 } + | /* empty */ { 0l, fun c -> [] } + | local_type local_type_list { I32.add (fst $2) 1l, fun c -> $1 c :: snd $2 c } -/* Tables, Memories & Globals */ +/* Tables, Memories, Globals, Tags */ table_use : | LPAR TABLE var RPAR { fun c -> $3 c } @@ -1066,7 +1207,7 @@ table_fields : | table_type const_expr1 { fun c x loc -> [{ttype = $1 c; tinit = $2 c} @@ loc], [], [], [] } | table_type /* Sugar */ - { fun c x loc -> let TableT (_, (_, ht)) as ttype = $1 c in + { fun c x loc -> let TableT (_, _, (_, ht)) as ttype = $1 c in [{ttype; tinit = [RefNull ht @@ loc] @@ loc} @@ loc], [], [], [] } | inline_import table_type /* Sugar */ { fun c x loc -> @@ -1076,26 +1217,26 @@ table_fields : | inline_export table_fields /* Sugar */ { fun c x loc -> let tabs, elems, ims, exs = $2 c x loc in tabs, elems, ims, $1 (TableExport x) c :: exs } - | ref_type LPAR ELEM elem_expr elem_expr_list RPAR /* Sugar */ + | addr_type ref_type LPAR ELEM elem_expr elem_expr_list RPAR /* Sugar */ { fun c x loc -> - let offset = [i32_const (0l @@ loc) @@ loc] @@ loc in - let einit = $4 c :: $5 c in - let size = Lib.List32.length einit in + let offset = [at_const $1 (0L @@ loc) @@ loc] @@ loc in + let einit = $5 c :: $6 c in + let size = Lib.List64.length einit in let emode = Active {index = x; offset} @@ loc in - let (_, ht) as etype = $1 c in + let (_, ht) as etype = $2 c in let tinit = [RefNull ht @@ loc] @@ loc in - [{ttype = TableT ({min = size; max = Some size}, etype); tinit} @@ loc], + [{ttype = TableT ($1, {min = size; max = Some size}, etype); tinit} @@ loc], [{etype; einit; emode} @@ loc], [], [] } - | ref_type LPAR ELEM elem_var_list RPAR /* Sugar */ + | addr_type ref_type LPAR ELEM elem_var_list RPAR /* Sugar */ { fun c x loc -> - let offset = [i32_const (0l @@ loc) @@ loc] @@ loc in - let einit = $4 c in - let size = Lib.List32.length einit in - let emode = Active {index = x; offset} @@ loc in - let (_, ht) as etype = $1 c in + let (_, ht) as etype = $2 c in let tinit = [RefNull ht @@ loc] @@ loc in - [{ttype = TableT ({min = size; max = Some size}, etype); tinit} @@ loc], + let offset = [at_const $1 (0L @@ loc) @@ loc] @@ loc in + let einit = $5 c in + let size = Lib.List64.length einit in + let emode = Active {index = x; offset} @@ loc in + [{ttype = TableT ($1, {min = size; max = Some size}, etype); tinit} @@ loc], [{etype; einit; emode} @@ loc], [], [] } @@ -1128,12 +1269,12 @@ memory_fields : | inline_export memory_fields /* Sugar */ { fun c x loc -> let mems, data, ims, exs = $2 c x loc in mems, data, ims, $1 (MemoryExport x) c :: exs } - | LPAR DATA string_list RPAR /* Sugar */ + | addr_type LPAR DATA string_list RPAR /* Sugar */ { fun c x loc -> - let offset = [i32_const (0l @@ loc) @@ loc] @@ loc in - let size = Int32.(div (add (of_int (String.length $3)) 65535l) 65536l) in - [{mtype = MemoryT {min = size; max = Some size}} @@ loc], - [{dinit = $3; dmode = Active {index = x; offset} @@ loc} @@ loc], + let size = Int64.(div (add (of_int (String.length $4)) 65535L) 65536L) in + let offset = [at_const $1 (0L @@ loc) @@ loc] @@ loc in + [{mtype = MemoryT ($1, {min = size; max = Some size})} @@ loc], + [{dinit = $4; dmode = Active {index = x; offset} @@ loc} @@ loc], [], [] } tag : @@ -1142,43 +1283,29 @@ tag : tag_fields : | type_use func_type - { fun c x at -> - let tgtype = inline_func_type_explicit c ($1 c) ($2 c) at in - [{tgtype} @@ at], [], [] } + { fun c x loc -> + let tgtype = inline_func_type_explicit c ($1 c) ($2 c) $loc($1) in + [{tgtype} @@ loc], [], [] } | func_type /* Sugar */ - { fun c x at -> - let tgtype = inline_func_type c ($1 c) at in - [{tgtype} @@ at], [], [] } - | inline_import type_use tag_fields_import /* Sugar */ - { fun c x at -> - let y = inline_func_type_explicit c ($2 c) ($3 c) at in + { fun c x loc -> + let tgtype = inline_func_type c ($1 c) $sloc in + [{tgtype} @@ loc], [], [] } + | inline_import type_use func_type /* Sugar */ + { fun c x loc -> + let y = inline_func_type_explicit c ($2 c) ($3 c) $loc($2) in [], [{ module_name = fst $1; item_name = snd $1; - idesc = TagImport y @@ at } @@ at ], [] } - | inline_import tag_fields_import /* Sugar */ - { fun c x at -> - let y = inline_func_type c ($2 c) at in + idesc = TagImport y @@ loc } @@ loc ], [] } + | inline_import func_type /* Sugar */ + { fun c x loc -> + let y = inline_func_type c ($2 c) $loc($2) in [], [{ module_name = fst $1; item_name = snd $1; - idesc = TagImport y @@ at } @@ at ], [] } + idesc = TagImport y @@ loc } @@ loc ], [] } | inline_export tag_fields /* Sugar */ - { fun c x at -> - let tgs, ims, exs = $2 c x at in tgs, ims, $1 (TagExport x) c :: exs } - -tag_fields_import : /* Sugar */ - | tag_fields_import_result { $1 } - | LPAR PARAM val_type_list RPAR tag_fields_import - { fun c -> let FuncT (ts1, ts2) = $5 c in - FuncT (snd $3 c @ ts1, ts2) } - | LPAR PARAM bind_var val_type RPAR tag_fields_import /* Sugar */ - { fun c -> let FuncT (ts1, ts2) = $6 c in - FuncT ($4 c :: ts1, ts2) } + { fun c x loc -> + let tgs, ims, exs = $2 c x loc in tgs, ims, $1 (TagExport x) c :: exs } -tag_fields_import_result : /* Sugar */ - | /* empty */ { fun c -> FuncT ([], []) } - | LPAR RESULT val_type_list RPAR tag_fields_import_result - { fun c -> - let FuncT (ts1, ts2) = $5 c in FuncT (ts1, snd $3 c @ ts2) } global : | LPAR GLOBAL bind_var_opt global_fields RPAR @@ -1197,7 +1324,6 @@ global_fields : { fun c x loc -> let globs, ims, exs = $2 c x loc in globs, ims, $1 (GlobalExport x) c :: exs } - /* Imports & Exports */ import_desc : @@ -1235,8 +1361,8 @@ export_desc : | LPAR FUNC var RPAR { fun c -> FuncExport ($3 c func) } | LPAR TABLE var RPAR { fun c -> TableExport ($3 c table) } | LPAR MEMORY var RPAR { fun c -> MemoryExport ($3 c memory) } - | LPAR TAG var RPAR { fun c -> TagExport ($3 c tag) } | LPAR GLOBAL var RPAR { fun c -> GlobalExport ($3 c global) } + | LPAR TAG var RPAR { fun c -> TagExport ($3 c tag) } export : | LPAR EXPORT name export_desc RPAR @@ -1360,61 +1486,112 @@ module_fields1 : {m with exports = $1 c :: m.exports} } module_var : - | VAR { $1 @@ $sloc } /* Sugar */ + | VAR { var $1 $sloc } /* Sugar */ module_ : | LPAR MODULE option(module_var) module_fields RPAR - { $3, Textual ($4 (empty_context ()) () () @@ $sloc) @@ $sloc } + { let m = $4 (empty_context ()) () () @@ $sloc in + $3, Textual (m, parse_annots m) @@ $sloc } inline_module : /* Sugar */ - | module_fields { Textual ($1 (empty_context ()) () () @@ $sloc) @@ $sloc } + | module_fields + { let m = $1 (empty_context ()) () () @@ $sloc in + (* Hack to handle annotations before first and after last token *) + let all = all_region (at $sloc).left.file in + Textual (m, parse_annots Source.(m.it @@ all)) @@ $sloc } inline_module1 : /* Sugar */ - | module_fields1 { Textual ($1 (empty_context ()) () () @@ $sloc) @@ $sloc } + | module_fields1 + { let m = $1 (empty_context ()) () () @@ $sloc in + (* Hack to handle annotations before first and after last token *) + let all = all_region (at $sloc).left.file in + Textual (m, parse_annots Source.(m.it @@ all)) @@ $sloc } /* Scripts */ script_var : - | VAR { $1 @@ $sloc } /* Sugar */ + | VAR { var $1 $sloc } /* Sugar */ + +instance_var : + | VAR { var $1 $sloc } /* Sugar */ + +definition_opt : + | DEFINITION { true } + | /* empty */ { false } script_module : - | module_ { $1 } - | LPAR MODULE option(module_var) BIN string_list RPAR - { $3, Encoded ("binary:" ^ string_of_pos (at $sloc).left, $5) @@ $sloc } - | LPAR MODULE option(module_var) QUOTE string_list RPAR - { $3, Quoted ("quote:" ^ string_of_pos (at $sloc).left, $5) @@ $sloc } + | LPAR MODULE definition_opt option(module_var) module_fields RPAR + { let m = $5 (empty_context ()) () () @@ $sloc in + $3, $4, Textual (m, parse_annots m) @@ $sloc } + | LPAR MODULE definition_opt option(module_var) BIN string_list RPAR + { let s = $6 @@ $loc($5) in + $3, $4, Encoded ("binary:" ^ string_of_pos (at $sloc).left, s) @@ $sloc } + | LPAR MODULE definition_opt option(module_var) QUOTE string_list RPAR + { let s = $6 @@ $loc($5) in + $3, $4, Quoted ("quote:" ^ string_of_pos (at $sloc).left, s) @@ $sloc } + +script_instance : + | instance { [], $1 } + | script_module /* sugar */ + { let isdef, var_opt, m = $1 in + if isdef then error (at $sloc) "misplaced module definition"; + [Module (None, m) @@ $sloc], (var_opt, None) } + +instance : + | LPAR MODULE INSTANCE instance_var module_var RPAR + { Some $4, Some $5 } + | LPAR MODULE INSTANCE module_var RPAR + { None, Some $4 } + | LPAR MODULE INSTANCE RPAR + { None, None } action : - | LPAR INVOKE option(module_var) name list(literal) RPAR + | LPAR INVOKE option(instance_var) name list(literal) RPAR { Invoke ($3, $4, $5) @@ $sloc } - | LPAR GET option(module_var) name RPAR + | LPAR GET option(instance_var) name RPAR { Get ($3, $4) @@ $sloc } + assertion : | LPAR ASSERT_MALFORMED script_module STRING RPAR - { AssertMalformed (snd $3, $4) @@ $sloc } + { [], AssertMalformed (thd $3, $4) @@ $sloc } | LPAR ASSERT_INVALID script_module STRING RPAR - { AssertInvalid (snd $3, $4) @@ $sloc } - | LPAR ASSERT_UNLINKABLE script_module STRING RPAR - { AssertUnlinkable (snd $3, $4) @@ $sloc } - | LPAR ASSERT_TRAP script_module STRING RPAR - { AssertUninstantiable (snd $3, $4) @@ $sloc } - | LPAR ASSERT_RETURN action list(result) RPAR { AssertReturn ($3, $4) @@ $sloc } + { [], AssertInvalid (thd $3, $4) @@ $sloc } + | LPAR ASSERT_MALFORMED_CUSTOM script_module STRING RPAR + { [], AssertMalformedCustom (thd $3, $4) @@ $sloc } + | LPAR ASSERT_INVALID_CUSTOM script_module STRING RPAR + { [], AssertInvalidCustom (thd $3, $4) @@ $sloc } + | LPAR ASSERT_UNLINKABLE script_instance STRING RPAR + { fst $3, AssertUnlinkable (snd (snd $3), $4) @@ $sloc } + | LPAR ASSERT_TRAP script_instance STRING RPAR + { fst $3, AssertUninstantiable (snd (snd $3), $4) @@ $sloc } + | LPAR ASSERT_RETURN action list(result) RPAR + { [], AssertReturn ($3, $4) @@ $sloc } | LPAR ASSERT_EXCEPTION action RPAR - { AssertException $3 @@ $sloc } - | LPAR ASSERT_TRAP action STRING RPAR { AssertTrap ($3, $4) @@ $sloc } - | LPAR ASSERT_EXHAUSTION action STRING RPAR { AssertExhaustion ($3, $4) @@ $sloc } + { [], AssertException $3 @@ $sloc } + | LPAR ASSERT_TRAP action STRING RPAR + { [], AssertTrap ($3, $4) @@ $sloc } + | LPAR ASSERT_EXHAUSTION action STRING RPAR + { [], AssertExhaustion ($3, $4) @@ $sloc } + | LPAR ASSERT_SUSPENSION action STRING RPAR + { [], AssertSuspension ($3, $4) @@ $sloc } cmd : - | action { Action $1 @@ $sloc } - | assertion { Assertion $1 @@ $sloc } - | script_module { Module (fst $1, snd $1) @@ $sloc } - | LPAR REGISTER name option(module_var) RPAR { Register ($3, $4) @@ $sloc } - | meta { Meta $1 @@ $sloc } + | action { [Action $1 @@ $sloc] } + | assertion { fst $1 @ [Assertion (snd $1) @@ $sloc] } + | script_module + { let isdef, var_opt, m = $1 in + if isdef then + [Module (var_opt, m) @@ $sloc] + else (* sugar *) + [Module (var_opt, m) @@ $sloc; Instance (var_opt, var_opt) @@ $sloc] } + | instance { [Instance (fst $1, snd $1) @@ $sloc] } + | LPAR REGISTER name option(instance_var) RPAR { [Register ($3, $4) @@ $sloc] } + | meta { [Meta $1 @@ $sloc] } meta : - | LPAR SCRIPT option(script_var) list(cmd) RPAR { Script ($3, $4) @@ $sloc } + | LPAR SCRIPT option(script_var) list(cmd) RPAR { Script ($3, List.concat $4) @@ $sloc } | LPAR INPUT option(script_var) STRING RPAR { Input ($3, $4) @@ $sloc } | LPAR OUTPUT option(script_var) STRING RPAR { Output ($3, Some $4) @@ $sloc } | LPAR OUTPUT option(script_var) RPAR { Output ($3, None) @@ $sloc } @@ -1449,7 +1626,7 @@ result : | LPAR REF_STRUCT RPAR { RefResult (RefTypePat StructHT) @@ $sloc } | LPAR REF_ARRAY RPAR { RefResult (RefTypePat ArrayHT) @@ $sloc } | LPAR REF_FUNC RPAR { RefResult (RefTypePat FuncHT) @@ $sloc } -/*| LPAR REF_EXN RPAR { RefResult (RefTypePat ExnRefType) @@ $sloc }*/ + | LPAR REF_EXN RPAR { RefResult (RefTypePat ExnHT) @@ $sloc } | LPAR REF_EXTERN RPAR { RefResult (RefTypePat ExternHT) @@ $sloc } | LPAR REF_NULL RPAR { RefResult NullPat @@ $sloc } | LPAR VEC_CONST VEC_SHAPE list(numpat) RPAR @@ -1457,13 +1634,14 @@ result : error (at $sloc) "wrong number of lane literals"; VecResult (VecPat (Value.V128 ($3, List.map (fun lit -> lit $3) $4))) @@ $sloc } + | LPAR EITHER result list(result) RPAR { EitherResult ($3 :: $4) @@ $sloc } script : - | list(cmd) EOF { $1 } + | list(cmd) EOF { List.concat $1 } | inline_module1 EOF { [Module (None, $1) @@ $sloc] } /* Sugar */ script1 : - | cmd { [$1] } + | cmd { $1 } module1 : | module_ EOF { $1 } diff --git a/interpreter/text/print.ml b/interpreter/text/print.ml index 949618214..44bf4e575 100644 --- a/interpreter/text/print.ml +++ b/interpreter/text/print.ml @@ -1,5 +1,6 @@ let instr oc width e = Sexpr.output oc width (Arrange.instr e) let func oc width f = Sexpr.output oc width (Arrange.func f) let module_ oc width m = Sexpr.output oc width (Arrange.module_ m) +let module_with_custom oc width m_cs = Sexpr.output oc width (Arrange.module_with_custom m_cs) let script oc width mode s = List.iter (Sexpr.output oc width) (Arrange.script mode s) diff --git a/interpreter/text/print.mli b/interpreter/text/print.mli index 861ae40d9..c97dc9f61 100644 --- a/interpreter/text/print.mli +++ b/interpreter/text/print.mli @@ -1,4 +1,5 @@ val instr : out_channel -> int -> Ast.instr -> unit val func : out_channel -> int -> Ast.func -> unit val module_ : out_channel -> int -> Ast.module_ -> unit +val module_with_custom : out_channel -> int -> Ast.module_ * Custom.section list -> unit val script : out_channel -> int -> [`Textual | `Binary] -> Script.script -> unit diff --git a/interpreter/util/lib.ml b/interpreter/util/lib.ml index a8876b3ef..2068e3bc3 100644 --- a/interpreter/util/lib.ml +++ b/interpreter/util/lib.ml @@ -11,16 +11,13 @@ struct if n = 0 then x else repeat (n - 1) f (f x) end -module Int = +module Char = struct - let log2 n = - if n <= 0 then failwith "log2"; - let rec loop acc n = if n = 1 then acc else loop (acc + 1) (n lsr 1) in - loop 0 n - - let is_power_of_two n = - if n < 0 then failwith "is_power_of_two"; - n <> 0 && n land (n - 1) = 0 + let is_digit_ascii c = '0' <= c && c <= '9' + let is_uppercase_ascii c = 'A' <= c && c <= 'Z' + let is_lowercase_ascii c = 'a' <= c && c <= 'z' + let is_letter_ascii c = is_uppercase_ascii c || is_lowercase_ascii c + let is_alphanum_ascii c = is_digit_ascii c || is_letter_ascii c end module String = @@ -83,6 +80,11 @@ struct | n, y::ys' when n > 0 -> split' (n - 1) (y::xs) ys' | _ -> failwith "split" + let rec last_opt = function + | x::[] -> Some x + | _::xs -> last_opt xs + | [] -> None + let rec lead = function | x::[] -> [] | x::xs -> x :: lead xs @@ -107,9 +109,15 @@ struct let index_of x = index_where ((=) x) - let rec pairwise f = function + let rec map3 f xs ys zs = + match xs, ys, zs with + | [], [], [] -> [] + | x::xs', y::ys', z::zs' -> f x y z :: map3 f xs' ys' zs' + | _ -> raise (Invalid_argument "Lib.List.map3") + + let rec map_pairwise f = function | [] -> [] - | x1::x2::xs -> f x1 x2 :: pairwise f xs + | x1::x2::xs -> f x1 x2 :: map_pairwise f xs | _ -> failwith "pairwise" end @@ -174,23 +182,64 @@ struct let index_of x = index_where ((=) x) end -module Array32 = +module List64 = +struct + let rec init n f = init' n f [] + and init' n f xs = + if n = 0L then xs else init' (Int64.sub n 1L) f (f (Int64.sub n 1L) :: xs) + + let rec make n x = make' n x [] + and make' n x xs = + if n = 0L then xs else make' (Int64.sub n 1L) x (x::xs) + + let rec length xs = length' xs 0L + and length' xs n = + match xs with + | [] -> n + | _::xs' when n < Int64.max_int -> length' xs' (Int64.add n 1L) + | _ -> failwith "length" + + let rec nth xs n = + match n, xs with + | 0L, x::_ -> x + | n, _::xs' when n > 0L -> nth xs' (Int64.sub n 1L) + | _ -> failwith "nth" + + let rec take n xs = + match n, xs with + | 0L, _ -> [] + | n, x::xs' when n > 0L -> x :: take (Int64.sub n 1L) xs' + | _ -> failwith "take" + + let rec drop n xs = + match n, xs with + | 0L, _ -> xs + | n, _::xs' when n > 0L -> drop (Int64.sub n 1L) xs' + | _ -> failwith "drop" + + let rec mapi f xs = mapi' f 0L xs + and mapi' f i = function + | [] -> [] + | x::xs -> f i x :: mapi' f (Int64.add i 1L) xs +end + +module Array64 = struct let make n x = - if n < 0l || Int64.of_int32 n > Int64.of_int max_int then - invalid_arg "Array32.make"; - Array.make (Int32.to_int n) x + if n < 0L || n > Int64.of_int max_int then + invalid_arg "Array64.make"; + Array.make (Int64.to_int n) x - let length a = Int32.of_int (Array.length a) + let length a = Int64.of_int (Array.length a) - let index_of_int32 i = - if i < 0l || Int64.of_int32 i > Int64.of_int max_int then -1 else - Int32.to_int i + let index_of_int64 i = + if i < 0L || i > Int64.of_int max_int then -1 else + Int64.to_int i - let get a i = Array.get a (index_of_int32 i) - let set a i x = Array.set a (index_of_int32 i) x + let get a i = Array.get a (index_of_int64 i) + let set a i x = Array.set a (index_of_int64 i) x let blit a1 i1 a2 i2 n = - Array.blit a1 (index_of_int32 i1) a2 (index_of_int32 i2) (index_of_int32 n) + Array.blit a1 (index_of_int64 i1) a2 (index_of_int64 i2) (index_of_int64 n) end module Bigarray = @@ -248,3 +297,45 @@ struct let value_opt p = !p let value p = match !p with Some x -> x | None -> raise Promise end + +module Int = +struct + let log2 n = + if n <= 0 then failwith "log2"; + let rec loop acc n = if n = 1 then acc else loop (acc + 1) (n lsr 1) in + loop 0 n + + let is_power_of_two n = + if n < 0 then failwith "is_power_of_two"; + n <> 0 && n land (n - 1) = 0 +end + +module Int32 = +struct + let log2 n = + if n <= 0l then failwith "log2"; + let rec loop acc n = + if n = 1l then acc else loop (Int32.add acc 1l) (Int32.shift_right_logical n 1) in + loop 0l n + + let is_power_of_two n = + if n < 0l then failwith "is_power_of_two"; + n <> 0l && Int32.(logand n (sub n 1l)) = 0l +end + +module Int64 = +struct + let log2_unsigned n = + let rec loop acc n = + if n = 1L then acc else loop (Int64.add acc 1L) (Int64.shift_right_logical n 1) in + loop 0L n + + let log2 n = + if n <= 0L then failwith "log2" else log2_unsigned n + + let is_power_of_two_unsigned n = + n <> 0L && Int64.(logand n (sub n 1L)) = 0L + + let is_power_of_two n = + if n < 0L then failwith "is_power_of_two" else is_power_of_two_unsigned n +end diff --git a/interpreter/util/lib.mli b/interpreter/util/lib.mli index bc7be3132..5044428af 100644 --- a/interpreter/util/lib.mli +++ b/interpreter/util/lib.mli @@ -19,13 +19,16 @@ sig val drop : int -> 'a list -> 'a list (* raises Failure *) val split : int -> 'a list -> 'a list * 'a list (* raises Failure *) + val last_opt : 'a list -> 'a option val lead : 'a list -> 'a list (* raises Failure *) val last : 'a list -> 'a (* raises Failure *) val split_last : 'a list -> 'a list * 'a (* raises Failure *) val index_of : 'a -> 'a list -> int option val index_where : ('a -> bool) -> 'a list -> int option - val pairwise : ('a -> 'a -> 'b) -> 'a list -> 'b list + + val map3 : ('a -> 'b -> 'c -> 'd) -> 'a list -> 'b list -> 'c list -> 'd list + val map_pairwise : ('a -> 'a -> 'b) -> 'a list -> 'b list end module List32 : @@ -44,13 +47,24 @@ sig val index_where : ('a -> bool) -> 'a list -> int32 option end -module Array32 : +module List64 : +sig + val init : int64 -> (int64 -> 'a) -> 'a list + val make : int64 -> 'a -> 'a list + val length : 'a list -> int64 + val nth : 'a list -> int64 -> 'a (* raises Failure *) + val take : int64 -> 'a list -> 'a list (* raises Failure *) + val drop : int64 -> 'a list -> 'a list (* raises Failure *) + val mapi : (int64 -> 'a -> 'b) -> 'a list -> 'b list +end + +module Array64 : sig - val make : int32 -> 'a -> 'a array - val length : 'a array -> int32 - val get : 'a array -> int32 -> 'a - val set : 'a array -> int32 -> 'a -> unit - val blit : 'a array -> int32 -> 'a array -> int32 -> int32 -> unit + val make : int64 -> 'a -> 'a array + val length : 'a array -> int64 + val get : 'a array -> int64 -> 'a + val set : 'a array -> int64 -> 'a -> unit + val blit : 'a array -> int64 -> 'a array -> int64 -> int64 -> unit end module Bigarray : @@ -81,6 +95,29 @@ sig val is_power_of_two : int -> bool end +module Int32 : +sig + val log2 : int32 -> int32 + val is_power_of_two : int32 -> bool +end + +module Int64 : +sig + val log2 : int64 -> int64 + val log2_unsigned : int64 -> int64 + val is_power_of_two : int64 -> bool + val is_power_of_two_unsigned : int64 -> bool +end + +module Char : +sig + val is_digit_ascii : char -> bool + val is_uppercase_ascii : char -> bool + val is_lowercase_ascii : char -> bool + val is_letter_ascii : char -> bool + val is_alphanum_ascii : char -> bool +end + module String : sig val implode : char list -> string diff --git a/interpreter/util/source.ml b/interpreter/util/source.ml index a3dc54c09..cb4b2e037 100644 --- a/interpreter/util/source.ml +++ b/interpreter/util/source.ml @@ -11,6 +11,10 @@ let at phrase = phrase.at let no_pos = {file = ""; line = 0; column = 0} let no_region = {left = no_pos; right = no_pos} +let all_region file = + { left = {file; line = 0; column = 0}; + right = {file; line = Int.max_int; column = Int.max_int} + } let string_of_pos pos = if pos.line = -1 then diff --git a/interpreter/util/source.mli b/interpreter/util/source.mli index a4bc79bc9..5d1734ea3 100644 --- a/interpreter/util/source.mli +++ b/interpreter/util/source.mli @@ -4,6 +4,7 @@ type 'a phrase = {at : region; it : 'a} val no_pos : pos val no_region : region +val all_region : string -> region val string_of_pos : pos -> string val string_of_region : region -> string diff --git a/interpreter/valid/match.ml b/interpreter/valid/match.ml index 70f8136c9..1e8d72e3c 100644 --- a/interpreter/valid/match.ml +++ b/interpreter/valid/match.ml @@ -13,6 +13,7 @@ let lookup c x = Lib.List32.nth c x let abs_of_str_type _c = function | DefStructT _ | DefArrayT _ -> StructHT | DefFuncT _ -> FuncHT + | DefContT _ -> ContHT let rec top_of_str_type c st = top_of_heap_type c (abs_of_str_type c st) @@ -22,10 +23,17 @@ and top_of_heap_type c = function | FuncHT | NoFuncHT -> FuncHT | ExnHT | NoExnHT -> ExnHT | ExternHT | NoExternHT -> ExternHT + | ContHT | NoContHT -> ContHT | DefHT dt -> top_of_str_type c (expand_def_type dt) | VarHT (StatX x) -> top_of_str_type c (expand_def_type (lookup c x)) | VarHT (RecX _) | BotHT -> assert false +let top_of_val_type c = function + | NumT _ as t -> t + | VecT _ as t -> t + | RefT (_, ht) -> RefT (Null, top_of_heap_type c ht) + | BotT -> BotT (* well.. *) + let rec bot_of_str_type c st = bot_of_heap_type c (abs_of_str_type c st) @@ -34,6 +42,7 @@ and bot_of_heap_type c = function | FuncHT | NoFuncHT -> NoFuncHT | ExnHT | NoExnHT -> NoExnHT | ExternHT | NoExternHT -> NoExternHT + | ContHT | NoContHT -> NoContHT | DefHT dt -> bot_of_str_type c (expand_def_type dt) | VarHT (StatX x) -> bot_of_str_type c (expand_def_type (lookup c x)) | VarHT (RecX _) | BotHT -> assert false @@ -47,11 +56,11 @@ let match_null _c nul1 nul2 = | _, _ -> nul1 = nul2 let match_limits _c lim1 lim2 = - I32.ge_u lim1.min lim2.min && + I64.ge_u lim1.min lim2.min && match lim1.max, lim2.max with | _, None -> true | None, Some _ -> false - | Some i, Some j -> I32.le_u i j + | Some i, Some j -> I64.le_u i j let match_num_type _c t1 t2 = @@ -73,6 +82,7 @@ let rec match_heap_type c t1 t2 = | NoFuncHT, t -> match_heap_type c t FuncHT | NoExnHT, t -> match_heap_type c t ExnHT | NoExternHT, t -> match_heap_type c t ExternHT + | NoContHT, t -> match_heap_type c t ContHT | VarHT (StatX x1), _ -> match_heap_type c (DefHT (lookup c x1)) t2 | _, VarHT (StatX x2) -> match_heap_type c t1 (DefHT (lookup c x2)) | DefHT dt1, DefHT dt2 -> match_def_type c dt1 dt2 @@ -85,6 +95,7 @@ let rec match_heap_type c t1 t2 = | DefArrayT _, EqHT -> true | DefArrayT _, ArrayHT -> true | DefFuncT _, FuncHT -> true + | DefContT _, ContHT -> true | _ -> false ) | BotHT, _ -> true @@ -107,7 +118,6 @@ and match_result_type c ts1 ts2 = List.length ts1 = List.length ts2 && List.for_all2 (match_val_type c) ts1 ts2 - and match_pack_type _c t1 t2 = t1 = t2 @@ -134,12 +144,15 @@ and match_array_type c (ArrayT ft1) (ArrayT ft2) = and match_func_type c (FuncT (ts11, ts12)) (FuncT (ts21, ts22)) = match_result_type c ts21 ts11 && match_result_type c ts12 ts22 +and match_cont_type c (ContT ht1) (ContT ht2) = + match_heap_type c ht1 ht2 and match_str_type c dt1 dt2 = match dt1, dt2 with | DefStructT st1, DefStructT st2 -> match_struct_type c st1 st2 | DefArrayT at1, DefArrayT at2 -> match_array_type c at1 at2 | DefFuncT ft1, DefFuncT ft2 -> match_func_type c ft1 ft2 + | DefContT ct1, DefContT ct2 -> match_cont_type c ct1 ct2 | _, _ -> false and match_def_type c dt1 dt2 = @@ -155,15 +168,15 @@ let match_global_type c (GlobalT (mut1, t1)) (GlobalT (mut2, t2)) = | Cons -> true | Var -> match_val_type c t2 t1 -let match_table_type c (TableT (lim1, t1)) (TableT (lim2, t2)) = - match_limits c lim1 lim2 && match_ref_type c t1 t2 && match_ref_type c t2 t1 - -let match_memory_type c (MemoryT lim1) (MemoryT lim2) = - match_limits c lim1 lim2 - let match_tag_type c (TagT dt1) (TagT dt2) = - match_def_type c dt1 dt2 && match_def_type c dt2 dt1 + match_def_type c dt1 dt2 + +let match_table_type c (TableT (at1, lim1, t1)) (TableT (at2, lim2, t2)) = + at1 = at2 && match_limits c lim1 lim2 && + match_ref_type c t1 t2 && match_ref_type c t2 t1 +let match_memory_type c (MemoryT (at1, lim1)) (MemoryT (at2, lim2)) = + at1 = at2 && match_limits c lim1 lim2 let match_extern_type c et1 et2 = match et1, et2 with diff --git a/interpreter/valid/match.mli b/interpreter/valid/match.mli index 5f0c96398..59e42a4c1 100644 --- a/interpreter/valid/match.mli +++ b/interpreter/valid/match.mli @@ -8,6 +8,7 @@ type context = def_type list (* Extremas *) +val top_of_val_type : context -> val_type -> val_type val top_of_heap_type : context -> heap_type -> heap_type val bot_of_heap_type : context -> heap_type -> heap_type val top_of_str_type : context -> str_type -> heap_type @@ -28,6 +29,7 @@ val match_str_type : context -> str_type -> str_type -> bool val match_def_type : context -> def_type -> def_type -> bool val match_func_type : context -> func_type -> func_type -> bool +val match_cont_type : context -> cont_type -> cont_type -> bool val match_table_type : context -> table_type -> table_type -> bool val match_memory_type : context -> memory_type -> memory_type -> bool diff --git a/interpreter/valid/valid.ml b/interpreter/valid/valid.ml index cbe1d6de5..aa6621ce4 100644 --- a/interpreter/valid/valid.ml +++ b/interpreter/valid/valid.ml @@ -46,8 +46,8 @@ let type_ (c : context) x = lookup "type" c.types x let func (c : context) x = lookup "function" c.funcs x let table (c : context) x = lookup "table" c.tables x let memory (c : context) x = lookup "memory" c.memories x -let tag (c : context) x = lookup "tag" c.tags x let global (c : context) x = lookup "global" c.globals x +let tag (c : context) x = lookup "tag" c.tags x let elem (c : context) x = lookup "elem segment" c.elems x let data (c : context) x = lookup "data segment" c.datas x let local (c : context) x = lookup "local" c.locals x @@ -67,7 +67,12 @@ let init_locals (c : context) xs = let func_type (c : context) x = match expand_def_type (type_ c x) with | DefFuncT ft -> ft - | _ -> error x.at ("non-function type " ^ I32.to_string_u x.it) + | _ -> error x.at ("non-function type " ^ Int32.to_string x.it) + +let cont_type (c : context) x = + match expand_def_type (type_ c x) with + | DefContT ct -> ct + | _ -> error x.at ("non-continuation type " ^ Int32.to_string x.it) let struct_type (c : context) x = match expand_def_type (type_ c x) with @@ -87,15 +92,38 @@ let refer category (s : Free.Set.t) x = let refer_func (c : context) x = refer "function" c.refs.Free.funcs x +(* Conversions *) + +let cont_type_of_heap_type (c : context) (ht : heap_type) at : cont_type = + match ht with + | DefHT dt -> as_cont_str_type (expand_def_type dt) + | VarHT (StatX x) -> cont_type c (x @@ at) + | _ -> assert false + +let func_type_of_heap_type (c : context) (ht : heap_type) at : func_type = + match ht with + | DefHT dt -> as_func_str_type (expand_def_type dt) + | VarHT (StatX x) -> func_type c (x @@ at) + | _ -> assert false + +let func_type_of_cont_type (c : context) (ContT ht) at : func_type = + func_type_of_heap_type c ht at + +let func_type_of_tag_type (c : context) (TagT dt) at : func_type = + match expand_def_type dt with + | DefFuncT ft -> ft + | _ -> error at "non-function type" + + (* Types *) let check_limits {min; max} range at msg = - require (I32.le_u min range) at msg; + require (I64.le_u min range) at msg; match max with | None -> () | Some max -> - require (I32.le_u max range) at msg; - require (I32.le_u min max) at + require (I64.le_u max range) at msg; + require (I64.le_u min max) at "size minimum must not be greater than maximum" let check_num_type (c : context) (t : num_type) at = @@ -108,6 +136,7 @@ let check_heap_type (c : context) (t : heap_type) at = match t with | AnyHT | NoneHT | EqHT | I31HT | StructHT | ArrayHT | FuncHT | NoFuncHT + | ContHT | NoContHT | ExnHT | NoExnHT | ExternHT | NoExternHT -> () | VarHT (StatX x) -> let _dt = type_ c (x @@ at) in () @@ -150,26 +179,45 @@ let check_func_type (c : context) (ft : func_type) at = check_result_type c ts1 at; check_result_type c ts2 at +let check_cont_type (c : context) (ct : cont_type) at = + match ct with + | ContT (VarHT (StatX x)) -> + let _dt = func_type c (x @@ at) in () + | _ -> assert false + let check_table_type (c : context) (tt : table_type) at = - let TableT (lim, t) = tt in - check_limits lim 0xffff_ffffl at "table size must be at most 2^32-1"; - check_ref_type c t at + let TableT (at_, lim, t) = tt in + check_ref_type c t at; + let sz, s = + match at_ with + | I32AT -> 0xffff_ffffL, "2^32-1 for i32" + | I64AT -> 0xffff_ffff_ffff_ffffL, "2^64-1 for i64" + in + check_limits lim sz at ("table size must be at most " ^ s) let check_memory_type (c : context) (mt : memory_type) at = - let MemoryT lim = mt in - check_limits lim 0x1_0000l at - "memory size must be at most 65536 pages (4GiB)" + let MemoryT (at_, lim) = mt in + let sz, s = + match at_ with + | I32AT -> 0x1_0000L, "2^16 pages (4 GiB) for i32" + | I64AT -> 0x1_0000_0000_0000L, "2^48 pages (256 TiB) for i64" + in + check_limits lim sz at ("memory size must be at most " ^ s) + +let check_tag_type (c : context) (et : tag_type) at = + match et with + | TagT dt -> let _ft = as_func_str_type (expand_def_type dt) in () let check_global_type (c : context) (gt : global_type) at = let GlobalT (_mut, t) = gt in check_val_type c t at - let check_str_type (c : context) (st : str_type) at = match st with | DefStructT st -> check_struct_type c st at | DefArrayT rt -> check_array_type c rt at | DefFuncT ft -> check_func_type c ft at + | DefContT ct -> check_cont_type c ct at let check_sub_type (c : context) (sut : sub_type) at = let SubT (_fin, hts, st) = sut in @@ -358,10 +406,18 @@ let check_memop (c : context) (memop : ('t, 's) memop) ty_size get_sz at = check_pack sz (ty_size memop.ty) at; Pack.packed_size sz in - require (1 lsl memop.align <= size) at + require (1 lsl memop.align >= 1 && 1 lsl memop.align <= size) at "alignment must not be larger than natural"; + let MemoryT (at_, _lim) = memory c (0l @@ at) in + if at_ = I32AT then + require (I64.lt_u memop.offset 0x1_0000_0000L) at + "offset out of range"; memop.ty +let check_cast (c : context) rt at = + require (not (match_ref_type c.types rt (Null, ContHT))) at + "invalid cast to continuation types" + (* * Conventions: @@ -383,6 +439,29 @@ let check_memop (c : context) (memop : ('t, 's) memop) ty_size get_sz at = * declarative typing rules. *) +let check_resume_table (c : context) ts2 (xys : (idx * hdl) list) at = + List.iter (fun (x1, x2) -> + match x2 with + | OnLabel x2 -> + let FuncT (ts3, ts4) = func_type_of_tag_type c (tag c x1) x1.at in + let ts' = label c x2 in + (match Lib.List.last_opt ts' with + | Some (RefT (nul', ((VarHT _ | DefHT _) as ht))) -> + let ct = cont_type_of_heap_type c ht x2.at in + let ft' = func_type_of_cont_type c ct x2.at in + require (match_func_type c.types (FuncT (ts4, ts2)) ft') x2.at + "type mismatch in continuation type"; + match_stack c (ts3 @ [RefT (nul', ht)]) ts' x2.at + | _ -> + error at + ("type mismatch: instruction requires concrete continuation reference type" ^ + " but label has " ^ string_of_result_type ts')) + | OnSwitch -> + let FuncT (ts3, ts4) = func_type_of_tag_type c (tag c x1) x1.at in + require (match_result_type c.types ts3 []) x1.at + "type mismatch tag type" + ) xys + let check_block_type (c : context) (bt : block_type) at : instr_type = match bt with | ValBlockType None -> InstrT ([], [], []) @@ -431,10 +510,12 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in (ts1 @ [NumT I32T]) --> ts2, List.map (fun x -> x @@ e.at) xs | Br x -> - label c x -->... [], [] + let ts = label c x in + ts -->... [], [] | BrIf x -> - (label c x @ [NumT I32T]) --> label c x, [] + let ts = label c x in + (ts @ [NumT I32T]) --> ts, [] | BrTable (xs, x) -> let n = List.length (label c x) in @@ -445,23 +526,26 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in | BrOnNull x -> let (_nul, ht) = peek_ref 0 s e.at in - (label c x @ [RefT (Null, ht)]) --> (label c x @ [RefT (NoNull, ht)]), [] + let ts = label c x in + (ts @ [RefT (Null, ht)]) --> (ts @ [RefT (NoNull, ht)]), [] | BrOnNonNull x -> let (_nul, ht) = peek_ref 0 s e.at in let t' = RefT (NoNull, ht) in - require (label c x <> []) e.at + let ts = label c x in + require (ts <> []) e.at ("type mismatch: instruction requires type " ^ string_of_val_type t' ^ " but label has " ^ string_of_result_type (label c x)); let ts0, t1 = Lib.List.split_last (label c x) in require (match_val_type c.types t' t1) e.at ("type mismatch: instruction requires type " ^ string_of_val_type t' ^ - " but label has " ^ string_of_result_type (label c x)); + " but label has " ^ string_of_result_type ts); (ts0 @ [RefT (Null, ht)]) --> ts0, [] | BrOnCast (x, rt1, rt2) -> check_ref_type c rt1 e.at; check_ref_type c rt2 e.at; + check_cast c rt2 e.at; require (match_ref_type c.types rt2 rt1) e.at ("type mismatch on cast: type " ^ string_of_ref_type rt2 ^ @@ -478,6 +562,7 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in | BrOnCastFail (x, rt1, rt2) -> check_ref_type c rt1 e.at; check_ref_type c rt2 e.at; + check_cast c rt2 e.at; let rt1' = diff_ref_type rt1 rt2 in require (match_ref_type c.types rt2 rt1) e.at @@ -504,12 +589,12 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in (ts1 @ [RefT (Null, DefHT (type_ c x))]) --> ts2, [] | CallIndirect (x, y) -> - let TableT (lim, t) = table c x in + let TableT (at, _lim, t) = table c x in let FuncT (ts1, ts2) = func_type c y in require (match_ref_type c.types t (Null, FuncHT)) x.at ("type mismatch: instruction requires table of function type" ^ " but table has element type " ^ string_of_ref_type t); - (ts1 @ [NumT I32T]) --> ts2, [] + (ts1 @ [NumT (num_type_of_addr_type at)]) --> ts2, [] | ReturnCall x -> let FuncT (ts1, ts2) = as_func_str_type (expand_def_type (func c x)) in @@ -528,17 +613,82 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in (ts1 @ [RefT (Null, DefHT (type_ c x))]) -->... [], [] | ReturnCallIndirect (x, y) -> - let TableT (_lim, t) = table c x in + let TableT (at, _lim, t) = table c x in let FuncT (ts1, ts2) = func_type c y in require (match_result_type c.types ts2 c.results) e.at ("type mismatch: current function requires result type " ^ string_of_result_type c.results ^ " but callee returns " ^ string_of_result_type ts2); - (ts1 @ [NumT I32T]) -->... [], [] + (ts1 @ [NumT (num_type_of_addr_type at)]) -->... [], [] + + | ContNew x -> + let ContT ht = cont_type c x in + [RefT (Null, ht)] --> + [RefT (NoNull, VarHT (StatX x.it))], [] + + | ContBind (x, y) -> + let ct = cont_type c x in + let FuncT (ts1, ts2) = func_type_of_cont_type c ct x.at in + let ct' = cont_type c y in + let FuncT (ts1', _) as ft' = func_type_of_cont_type c ct' y.at in + require (List.length ts1 >= List.length ts1') x.at + "type mismatch in continuation arguments"; + let ts11, ts12 = Lib.List.split (List.length ts1 - List.length ts1') ts1 in + require (match_func_type c.types (FuncT (ts12, ts2)) ft') e.at + "type mismatch in continuation types"; + (ts11 @ [RefT (Null, VarHT (StatX x.it))]) --> + [RefT (NoNull, VarHT (StatX y.it))], [] + + | Suspend x -> + let tag = tag c x in + let FuncT (ts1, ts2) = func_type_of_tag_type c tag x.at in + ts1 --> ts2, [] + + | Resume (x, xys) -> + let ct = cont_type c x in + let FuncT (ts1, ts2) = func_type_of_cont_type c ct x.at in + check_resume_table c ts2 xys e.at; + (ts1 @ [RefT (Null, VarHT (StatX x.it))]) --> ts2, [] + + | ResumeThrow (x, y, xys) -> + let ct = cont_type c x in + let FuncT (ts1, ts2) = func_type_of_cont_type c ct x.at in + let tag = tag c y in + let FuncT (ts0, _) = func_type_of_tag_type c tag y.at in + check_resume_table c ts2 xys e.at; + (ts0 @ [RefT (Null, VarHT (StatX x.it))]) --> ts2, [] + + | ResumeThrowRef (x, xys) -> + let ct = cont_type c x in + let FuncT (_ts1, ts2) = func_type_of_cont_type c ct x.at in + check_resume_table c ts2 xys e.at; + ([RefT (Null, ExnHT); RefT (Null, VarHT (StatX x.it))]) --> ts2, [] + + | Switch (x, y) -> + let ct1 = cont_type c x in + let FuncT (ts11, ts12) = func_type_of_cont_type c ct1 x.at in + let FuncT (ts21, ts22) = + match Lib.List.last_opt ts11 with + | Some (RefT (nul', ht)) -> + func_type_of_cont_type c (cont_type_of_heap_type c ht x.at) x.at + | _ -> + error x.at + ("type mismatch: instruction requires continuation reference type" ^ + " but the type annotation has " ^ string_of_result_type ts11) + in + let et = tag c y in + let FuncT (ts31, t) = func_type_of_tag_type c et y.at in + require (ts31 = []) y.at + "type mismatch in switch tag"; + require (match_result_type c.types ts12 t) y.at + "type mismatch in continuation types"; + require (match_result_type c.types t ts22) y.at + "type mismatch in continuation types"; + let ts11' = Lib.List.lead ts11 in + (ts11' @ [RefT (Null, VarHT (StatX x.it))]) --> ts21, [] | Throw x -> - let TagT dt = tag c x in - let FuncT (ts1, ts2) = as_func_str_type (expand_def_type dt) in + let FuncT (ts1, ts2) = func_type_of_tag_type c (tag c x) x.at in ts1 -->... [], [] | ThrowRef -> @@ -574,100 +724,105 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in [t] --> [], [] | TableGet x -> - let TableT (_lim, rt) = table c x in - [NumT I32T] --> [RefT rt], [] + let TableT (at, _lim, rt) = table c x in + [NumT (num_type_of_addr_type at)] --> [RefT rt], [] | TableSet x -> - let TableT (_lim, rt) = table c x in - [NumT I32T; RefT rt] --> [], [] + let TableT (at, _lim, rt) = table c x in + [NumT (num_type_of_addr_type at); RefT rt] --> [], [] | TableSize x -> - let _tt = table c x in - [] --> [NumT I32T], [] + let TableT (at, _lim, _rt) = table c x in + [] --> [NumT (num_type_of_addr_type at)], [] | TableGrow x -> - let TableT (_lim, rt) = table c x in - [RefT rt; NumT I32T] --> [NumT I32T], [] + let TableT (at, _lim, rt) = table c x in + [RefT rt; NumT (num_type_of_addr_type at)] --> + [NumT (num_type_of_addr_type at)], [] | TableFill x -> - let TableT (_lim, rt) = table c x in - [NumT I32T; RefT rt; NumT I32T] --> [], [] + let TableT (at, _lim, rt) = table c x in + [NumT (num_type_of_addr_type at); RefT rt; + NumT (num_type_of_addr_type at)] --> [], [] | TableCopy (x, y) -> - let TableT (_lim1, t1) = table c x in - let TableT (_lim2, t2) = table c y in + let TableT (at1, _lim1, t1) = table c x in + let TableT (at2, _lim2, t2) = table c y in require (match_ref_type c.types t2 t1) x.at ("type mismatch: source element type " ^ string_of_ref_type t1 ^ " does not match destination element type " ^ string_of_ref_type t2); - [NumT I32T; NumT I32T; NumT I32T] --> [], [] + [NumT (num_type_of_addr_type at1); NumT (num_type_of_addr_type at2); + NumT (num_type_of_addr_type (min at1 at2))] --> [], [] | TableInit (x, y) -> - let TableT (_lim1, t1) = table c x in + let TableT (at, _lim1, t1) = table c x in let t2 = elem c y in require (match_ref_type c.types t2 t1) x.at ("type mismatch: element segment's type " ^ string_of_ref_type t1 ^ " does not match table's element type " ^ string_of_ref_type t2); - [NumT I32T; NumT I32T; NumT I32T] --> [], [] + [NumT (num_type_of_addr_type at); NumT I32T; NumT I32T] --> [], [] | ElemDrop x -> ignore (elem c x); [] --> [], [] | Load (x, memop) -> - let _mt = memory c x in + let MemoryT (at, _lim) = memory c x in let t = check_memop c memop num_size (Lib.Option.map fst) e.at in - [NumT I32T] --> [NumT t], [] + [NumT (num_type_of_addr_type at)] --> [NumT t], [] | Store (x, memop) -> - let _mt = memory c x in + let MemoryT (at, _lim) = memory c x in let t = check_memop c memop num_size (fun sz -> sz) e.at in - [NumT I32T; NumT t] --> [], [] + [NumT (num_type_of_addr_type at); NumT t] --> [], [] | VecLoad (x, memop) -> - let _mt = memory c x in + let MemoryT (at, _lim) = memory c x in let t = check_memop c memop vec_size (Lib.Option.map fst) e.at in - [NumT I32T] --> [VecT t], [] + [NumT (num_type_of_addr_type at)] --> [VecT t], [] | VecStore (x, memop) -> - let _mt = memory c x in + let MemoryT (at, _lim) = memory c x in let t = check_memop c memop vec_size (fun _ -> None) e.at in - [NumT I32T; VecT t] --> [], [] + [NumT (num_type_of_addr_type at); VecT t] --> [], [] | VecLoadLane (x, memop, i) -> - let _mt = memory c x in + let MemoryT (at, _lim) = memory c x in let t = check_memop c memop vec_size (fun sz -> Some sz) e.at in require (i < vec_size t / Pack.packed_size memop.pack) e.at "invalid lane index"; - [NumT I32T; VecT t] --> [VecT t], [] + [NumT (num_type_of_addr_type at); VecT t] --> [VecT t], [] | VecStoreLane (x, memop, i) -> - let _mt = memory c x in + let MemoryT (at, _lim) = memory c x in let t = check_memop c memop vec_size (fun sz -> Some sz) e.at in require (i < vec_size t / Pack.packed_size memop.pack) e.at "invalid lane index"; - [NumT I32T; VecT t] --> [], [] + [NumT (num_type_of_addr_type at); VecT t] --> [], [] | MemorySize x -> - let _mt = memory c x in - [] --> [NumT I32T], [] + let MemoryT (at, _lim) = memory c x in + [] --> [NumT (num_type_of_addr_type at)], [] | MemoryGrow x -> - let _mt = memory c x in - [NumT I32T] --> [NumT I32T], [] + let MemoryT (at, _lim) = memory c x in + [NumT (num_type_of_addr_type at)] --> [NumT (num_type_of_addr_type at)], [] | MemoryFill x -> - let _mt = memory c x in - [NumT I32T; NumT I32T; NumT I32T] --> [], [] + let MemoryT (at, _lim) = memory c x in + [NumT (num_type_of_addr_type at); NumT I32T; + NumT (num_type_of_addr_type at)] --> [], [] | MemoryCopy (x, y)-> - let _mt = memory c x in - let _mt = memory c y in - [NumT I32T; NumT I32T; NumT I32T] --> [], [] + let MemoryT (at1, _lib1) = memory c x in + let MemoryT (at2, _lib2) = memory c y in + [NumT (num_type_of_addr_type at1); NumT (num_type_of_addr_type at2); + NumT (num_type_of_addr_type (min at1 at2))] --> [], [] | MemoryInit (x, y) -> - let _mt = memory c x in + let MemoryT (at, _lib) = memory c x in let () = data c y in - [NumT I32T; NumT I32T; NumT I32T] --> [], [] + [NumT (num_type_of_addr_type at); NumT I32T; NumT I32T] --> [], [] | DataDrop x -> let () = data c x in @@ -693,11 +848,13 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in | RefTest rt -> let (_nul, ht) = rt in check_ref_type c rt e.at; + check_cast c rt e.at; [RefT (Null, top_of_heap_type c.types ht)] --> [NumT I32T], [] | RefCast rt -> let (nul, ht) = rt in check_ref_type c rt e.at; + check_cast c rt e.at; [RefT (Null, top_of_heap_type c.types ht)] --> [RefT (nul, ht)], [] | RefEq -> @@ -860,6 +1017,10 @@ let rec check_instr (c : context) (e : instr) (s : infer_result_type) : infer_in let t = VecT (type_vec binop) in [t; t] --> [t], [] + | VecTernary ternop -> + let t = VecT (type_vec ternop) in + [t; t; t] --> [t], [] + | VecCompare relop -> let t = VecT (type_vec relop) in [t; t] --> [t], [] @@ -933,17 +1094,16 @@ and check_catch (c : context) (cc : catch) (ts : val_type list) at = let match_target = match_resulttype "label" "catch handler" in match cc.it with | Catch (x1, x2) -> - let TagT dt = tag c x1 in - let FuncT (ts1, ts2) = as_func_str_type (expand_def_type dt) in + let FuncT (ts1, ts2) = func_type_of_tag_type c (tag c x1) x1.at in match_target c ts1 (label c x2) cc.at | CatchRef (x1, x2) -> let TagT dt = tag c x1 in let FuncT (ts1, ts2) = as_func_str_type (expand_def_type dt) in - match_target c (ts1 @ [RefT (Null, ExnHT)]) (label c x2) cc.at + match_target c (ts1 @ [RefT (NoNull, ExnHT)]) (label c x2) cc.at | CatchAll x -> match_target c [] (label c x) cc.at | CatchAllRef x -> - match_target c [RefT (Null, ExnHT)] (label c x) cc.at + match_target c [RefT (NoNull, ExnHT)] (label c x) cc.at (* Functions & Constants *) @@ -1009,7 +1169,7 @@ let check_global (c : context) (glob : global) : context = let check_table (c : context) (tab : table) : context = let {ttype; tinit} = tab.it in - let TableT (_lim, rt) = ttype in + let TableT (_at, _lim, rt) = ttype in check_table_type c ttype tab.at; check_const c tinit (RefT rt); {c with tables = c.tables @ [ttype]} @@ -1019,20 +1179,15 @@ let check_memory (c : context) (mem : memory) : context = check_memory_type c mtype mem.at; {c with memories = c.memories @ [mtype]} -let check_tag (c : context) (t : tag) : context = - let FuncT (_, ts2) = func_type c t.it.tgtype in - require (ts2 = []) t.it.tgtype.at "non-empty tag result type"; - {c with tags = c.tags @ [TagT (type_ c t.it.tgtype)]} - let check_elem_mode (c : context) (t : ref_type) (mode : segment_mode) = match mode.it with | Passive -> () | Active {index; offset} -> - let TableT (_lim, et) = table c index in + let TableT (at, _lim, et) = table c index in require (match_ref_type c.types t et) mode.at ("type mismatch: element segment's type " ^ string_of_ref_type t ^ " does not match table's element type " ^ string_of_ref_type et); - check_const c offset (NumT I32T) + check_const c offset (NumT (num_type_of_addr_type at)) | Declarative -> () let check_elem (c : context) (seg : elem_segment) : context = @@ -1046,8 +1201,8 @@ let check_data_mode (c : context) (mode : segment_mode) = match mode.it with | Passive -> () | Active {index; offset} -> - let _mt = memory c index in - check_const c offset (NumT I32T) + let MemoryT (at, _) = memory c index in + check_const c offset (NumT (num_type_of_addr_type at)) | Declarative -> assert false let check_data (c : context) (seg : data_segment) : context = @@ -1055,6 +1210,11 @@ let check_data (c : context) (seg : data_segment) : context = check_data_mode c dmode; {c with datas = c.datas @ [()]} +let check_tag (c : context) (tag : tag) : context = + check_tag_type c (TagT (type_ c tag.it.tgtype)) tag.at; + {c with tags = c.tags @ [TagT (type_ c tag.it.tgtype)]} + + (* Modules *) @@ -1108,6 +1268,7 @@ let check_module (m : module_) = |> check_list check_type m.it.types |> check_list check_import m.it.imports |> check_list check_func m.it.funcs + |> check_list check_tag m.it.tags |> check_list check_table m.it.tables |> check_list check_memory m.it.memories |> check_list check_global m.it.globals @@ -1118,3 +1279,7 @@ let check_module (m : module_) = List.iter (check_func_body c) m.it.funcs; Option.iter (check_start c) m.it.start; ignore (List.fold_left (check_export c) NameSet.empty m.it.exports) + +let check_module_with_custom ((m : module_), (cs : Custom.section list)) = + check_module m; + List.iter (fun (module S : Custom.Section) -> S.Handler.check m S.it) cs diff --git a/interpreter/valid/valid.mli b/interpreter/valid/valid.mli index 5827ae56e..ef8931331 100644 --- a/interpreter/valid/valid.mli +++ b/interpreter/valid/valid.mli @@ -1,3 +1,4 @@ exception Invalid of Source.region * string val check_module : Ast.module_ -> unit (* raises Invalid *) +val check_module_with_custom : Ast.module_ * Custom.section list -> unit (* raises Invalid, Custom.Check *) diff --git a/proposals/annotations/Overview.md b/proposals/annotations/Overview.md new file mode 100644 index 000000000..bd9940405 --- /dev/null +++ b/proposals/annotations/Overview.md @@ -0,0 +1,107 @@ +# Custom Annotation Syntax for the Wasm Text Format + +## Motivation + +Problem + +* The Wasm binary format supports custom sections to enable associating arbitrary meta data with a Wasm module. + +* No equivalent exists for the text format. In particular, there is no way to + - represent custom sections themselves in the text format, cf. WebAssembly/design#1153 and https://gist.github.com/binji/d1cfff7faaebb2aa4f8b1c995234e5a0 + - reflect arbitrary names in the text format, cf. WebAssembly/spec#617 + - express information like for Web IDL bindings, cf. https://github.com/WebAssembly/webidl-bindings/blob/master/proposals/webidl-bindings/Explainer.md + +Solution + +* This proposal adds the ability to decorate a module in textual notarion with arbitrary annotations of the form `(@id ...)`. + +* Neither the syntactic shape nor the semantics is prescribed by the Wasm specification, though the Appendix might include a description of optional support for name section annotations and generic custom sections. + +* As an aside, the syntax of symbolic identifiers is extended to allow arbitrary strings in the form `$"..."`. + +* This proposal only affects the text format, nothing else. + + +## Details + +Extend the Text Format as follows: + +* Anywhere where white space is allowed, allow *annotations* of the following form: + ``` + annot ::= "(@"annotid annotelem* ")" + annotid ::= idchar+ | name + annotelem ::= keyword | reserved | uN | sN | fN | string | id | "(" annotelem* ")" + ``` + In other words, an annotation can contain any sequence of tokens, as long as it is well-bracketed. + No white space is allowed as part of the initial `(@annotid` delimiter. + +* The initial `annotid` is meant to be an identifier categorising the extension, and plays a role similar to the name of a custom section. + By convention, annotations corresponding to a custom section should use the same id. + +* Extend the grammar of identifiers as follows: + ``` + id ::= "$"idchar+ | "$"name + ``` + +* Elaborate identifiers to their denotation as a name, treating the unquoted form as a shorthand for the name `"idchar+"`. In all places where identifiers are compared, compare the denotated names instead. In particular, change the identifier environment `I` to record names instead of identifiers. + +Extend the Appendix on the Custom Sections: + +* Define annotations reflecting the Name section, which take the form of annotations `(@name "name")`. + They may be placed after the binder for any construct that can be named by the name section. + +* Define annotation syntax expressing arbitrary custom sections; cf. https://gist.github.com/binji/d1cfff7faaebb2aa4f8b1c995234e5a0 + + With that, a custom section annotation is specified as follows: + ``` + custom ::= '(' '@custom' string place? datastring ')' + place ::= + | '(' 'before' section ')' + | '(' 'after' section ')' + | '(' 'before' 'first' ')' + | '(' 'after' 'last' ')' + section ::= + | 'type' + | 'import' + | 'func' + | 'table' + | 'memory' + | 'global' + | 'export' + | 'start' + | 'elem' + | 'code' + | 'data' + | 'datacount' + ``` + If placement relative to an explicit section is used, then that section must exist in the encoding of the annotated module. + + Custom section annotations that appear within module fields rather than as siblings of module fields may be ignored. + + As with any matter concerning annotations, it is up to implementations how they handle the case where an explicit custom section overlaps with individual annotations that are associated with the same custom section. + + +## Examples + +Expressing generic custom sections (cf. https://gist.github.com/binji/d1cfff7faaebb2aa4f8b1c995234e5a0) +```wasm +(module + (@custom "my-fancy-section" (after func) "contents-bytes") +) +``` + +Expressing names +```wasm +(module (@name "Gümüsü") + (func $lambda (@name "λ") (param $x (@name "α βγ δ") i32) (result i32) (local.get $x)) +) +``` + +Web IDL bindings (cf. https://github.com/WebAssembly/webidl-bindings/blob/master/proposals/webidl-bindings/Explainer.md) +```wasm +(module + (func (export "f") (param i32 (@js unsigned)) ...) ;; argument converted as unsigned + (func (export "method") (param $x anyref (@js this)) (param $y i32) ...) ;; maps this to first arg + (func (import "m" "constructor") (@js new) (param i32) (result anyref) ;; is called as a constructor +) +``` diff --git a/proposals/branch-hinting/Motivation.md b/proposals/branch-hinting/Motivation.md new file mode 100644 index 000000000..a8e30d734 --- /dev/null +++ b/proposals/branch-hinting/Motivation.md @@ -0,0 +1,393 @@ +# Proposal motivation + +This document has the purpose of explaining better the underlying issues that the proposal +aim to solve, and why they can't be solved in other ways (for example with better tooling). + +# Example code + +We will refer to the following example code, which is purposefully simple to make +the analysis easier. The analysis generalizes for more complex code. + +``` +void foo(); +int run(int i) { + int ret = 0; + int b = 0; + while(i) { + if(__builtin_expect(b==i-1, 0)) { + foo(); + } + b++; + ret += b; + if(b>=i) + break; + } + return ret; +} +``` + +`__builtin_expect` is a gcc and clang specific builtin that hints the compiler that +we know that `b==i-1` is very likely to be false. + +The compiler can use this information to make better decisions regarding register allocation, +inlining, code layout, etc... + +For example here the compiler may decide that it is not worth it to inline the call to `foo()`, +and that in the resulting machine code the call should be placed far from the rest of the code. + +# Native x86-64 code generation + +We can see the effect of `__builtin_expect` when compiling native code by compiling +first with the expected value `1` and then `0`: + +``` +// expected 1 +0000000000000000 : + 0: 55 push rbp + 1: 41 57 push r15 + 3: 41 56 push r14 + 5: 53 push rbx + 6: 50 push rax + 7: 41 89 ff mov r15d,edi + a: 85 ff test edi,edi + c: bb 01 00 00 00 mov ebx,0x1 + 11: 41 be 01 00 00 00 mov r14d,0x1 + 17: 44 0f 4f f7 cmovg r14d,edi + 1b: 41 f7 de neg r14d + 1e: 31 ed xor ebp,ebp + 20: /----> 45 85 ff test r15d,r15d + 23: /--|----- 74 21 je 46 + 25: | | 41 39 df cmp r15d,ebx + 28: | | /-- 75 05 jne 2f + 2a: | | | e8 00 00 00 00 call 2f foo + 2f: | | \-> 01 dd add ebp,ebx + 31: | | 41 8d 04 1e lea eax,[r14+rbx*1] + 35: | | 83 c0 01 add eax,0x1 + 38: | | 89 d9 mov ecx,ebx + 3a: | | 83 c1 01 add ecx,0x1 + 3d: | | 89 cb mov ebx,ecx + 3f: | | 83 f8 01 cmp eax,0x1 + 42: | \----- 75 dc jne 20 + 44: | /-- eb 02 jmp 48 + 46: \-----|-> 31 ed xor ebp,ebp + 48: \-> 89 e8 mov eax,ebp + 4a: 48 83 c4 08 add rsp,0x8 + 4e: 5b pop rbx + 4f: 41 5e pop r14 + 51: 41 5f pop r15 + 53: 5d pop rbp + 54: c3 ret +``` + +``` +// expected 0 +0000000000000000 : + 0: 55 push rbp + 1: 41 57 push r15 + 3: 41 56 push r14 + 5: 53 push rbx + 6: 50 push rax + 7: 41 89 ff mov r15d,edi + a: 85 ff test edi,edi + c: bb 01 00 00 00 mov ebx,0x1 + 11: 41 be 01 00 00 00 mov r14d,0x1 + 17: 44 0f 4f f7 cmovg r14d,edi + 1b: 41 f7 de neg r14d + 1e: 31 ed xor ebp,ebp + 20: /-> 45 85 ff test r15d,r15d + 23: /-----------|-- 74 23 je 48 + 25: | | 41 39 df cmp r15d,ebx + 28: | /-----|-- 74 17 je 41 + 2a: | /--|-----|-> 01 dd add ebp,ebx + 2c: | | | | 41 8d 04 1e lea eax,[r14+rbx*1] + 30: | | | | 83 c0 01 add eax,0x1 + 33: | | | | 89 d9 mov ecx,ebx + 35: | | | | 83 c1 01 add ecx,0x1 + 38: | | | | 89 cb mov ebx,ecx + 3a: | | | | 83 f8 01 cmp eax,0x1 + 3d: | | | \-- 75 e1 jne 20 + 3f: | | | /----- eb 09 jmp 4a + 41: | | \--|----> e8 00 00 00 00 call 46 foo + 46: | \-----|----- eb e2 jmp 2a + 48: \--------|----> 31 ed xor ebp,ebp + 4a: \----> 89 e8 mov eax,ebp + 4c: 48 83 c4 08 add rsp,0x8 + 50: 5b pop rbx + 51: 41 5e pop r14 + 53: 41 5f pop r15 + 55: 5d pop rbp + 56: c3 ret +``` + +You can see that if the compiler expects the branch to be taken, it puts the call +with the rest of the loop code, while if it expects it to be not take, it puts it after. + +This is to maximise the likelyhood that the most often executed code is in the instruction cache. + +# Wasm code generation + +When compiling into Wasm, the compiler can still use `__builtin_expect` to make better decisions: + +For example it can still decide that inlining the call to `foo()` is not worth it. + +But a key difference is that in the case of Wasm, the Wasm code is not the actual machine +code that the cpu will execute. +Moreover, Wasm's structured control flow has no 1 to 1 relationship with the final machine code +that a Wasm engine will generate in tems of code layout. + +This means that there is no way that a Wasm compiler can tell a Wasm engine how it would +like the machine code to be laid out. + +The Wasm generated from the above code is the following (regardless of the presence of `__builtin_expect`): + +``` +func[2] : + local[0..3] type=i32 + local.get 0 + i32.const 4294967295 + i32.add + local.set 1 + local.get 0 + i32.const 0 + i32.ne + local.set 2 + i32.const 0 + local.tee 3 + local.tee 4 + loop + local.get 2 + if + local.get 4 + local.get 1 + i32.eq + if + call 0 + end + local.get 4 + i32.const 1 + i32.add + local.tee 4 + local.get 3 + i32.add + local.set 3 + local.get 4 + local.get 0 + i32.lt_s + br_if 1 + local.get 3 + return + end + end + end +``` + +And the machine code generated by V8's TurboFan is: + +``` +0x3ed39fc0d640 0 55 push rbp +0x3ed39fc0d641 1 4889e5 REX.W movq rbp,rsp +0x3ed39fc0d644 4 6a0a push 0xa +0x3ed39fc0d646 6 56 push rsi +0x3ed39fc0d647 7 4883ec20 REX.W subq rsp,0x20 +0x3ed39fc0d64b b 488b5e23 REX.W movq rbx,[rsi+0x23] +0x3ed39fc0d64f f 488945e8 REX.W movq [rbp-0x18],rax +0x3ed39fc0d653 13 483b23 REX.W cmpq rsp,[rbx] +0x3ed39fc0d656 16 0f869b000000 jna 0x3ed39fc0d6f7 <+0xb7> +0x3ed39fc0d65c 1c 83f800 cmpl rax,0x0 +0x3ed39fc0d65f 1f 0f95c3 setnzl bl +0x3ed39fc0d662 22 0fb6db movzxbl rbx,rbx +0x3ed39fc0d665 25 33c9 xorl rcx,rcx +0x3ed39fc0d667 27 48895dd0 REX.W movq [rbp-0x30],rbx +0x3ed39fc0d66b 2b 488bd1 REX.W movq rdx,rcx +0x3ed39fc0d66e 2e 488bf9 REX.W movq rdi,rcx +0x3ed39fc0d671 31 660f1f840000000000 nop +0x3ed39fc0d67a 3a 660f1f440000 nop +0x3ed39fc0d680 40 4c8b4623 REX.W movq r8,[rsi+0x23] +0x3ed39fc0d684 44 493b20 REX.W cmpq rsp,[r8] +0x3ed39fc0d687 47 0f867c000000 jna 0x3ed39fc0d709 <+0xc9> +0x3ed39fc0d68d 4d 837dd000 cmpl [rbp-0x30],0x0 +0x3ed39fc0d691 51 0f8454000000 jz 0x3ed39fc0d6eb <+0xab> +0x3ed39fc0d697 57 448d40ff leal r8,[rax-0x1] +0x3ed39fc0d69b 5b 443bc7 cmpl r8,rdi +0x3ed39fc0d69e 5e 0f8539000000 jnz 0x3ed39fc0d6dd <+0x9d> +0x3ed39fc0d6a4 64 448b462b movl r8,[rsi+0x2b] +0x3ed39fc0d6a8 68 4d03c5 REX.W addq r8,r13 +0x3ed39fc0d6ab 6b 458b4007 movl r8,[r8+0x7] +0x3ed39fc0d6af 6f 4d03c5 REX.W addq r8,r13 +0x3ed39fc0d6b2 72 4c8b4e2f REX.W movq r9,[rsi+0x2f] +0x3ed39fc0d6b6 76 4d8b09 REX.W movq r9,[r9] +0x3ed39fc0d6b9 79 48894de0 REX.W movq [rbp-0x20],rcx +0x3ed39fc0d6bd 7d 48897dd8 REX.W movq [rbp-0x28],rdi +0x3ed39fc0d6c1 81 4c8bd6 REX.W movq r10,rsi +0x3ed39fc0d6c4 84 498bf0 REX.W movq rsi,r8 +0x3ed39fc0d6c7 87 4d8bc2 REX.W movq r8,r10 +0x3ed39fc0d6ca 8a 41ffd1 call r9 +0x3ed39fc0d6cd 8d 488b45e8 REX.W movq rax,[rbp-0x18] +0x3ed39fc0d6d1 91 488b4de0 REX.W movq rcx,[rbp-0x20] +0x3ed39fc0d6d5 95 488b7dd8 REX.W movq rdi,[rbp-0x28] +0x3ed39fc0d6d9 99 488b75f0 REX.W movq rsi,[rbp-0x10] +0x3ed39fc0d6dd 9d 83c701 addl rdi,0x1 +0x3ed39fc0d6e0 a0 03cf addl rcx,rdi +0x3ed39fc0d6e2 a2 3bf8 cmpl rdi,rax +0x3ed39fc0d6e4 a4 7c9a jl 0x3ed39fc0d680 <+0x40> +0x3ed39fc0d6e6 a6 e907000000 jmp 0x3ed39fc0d6f2 <+0xb2> +0x3ed39fc0d6eb ab 33c0 xorl rax,rax +0x3ed39fc0d6ed ad 488be5 REX.W movq rsp,rbp +0x3ed39fc0d6f0 b0 5d pop rbp +0x3ed39fc0d6f1 b1 c3 retl +0x3ed39fc0d6f2 b2 488bc1 REX.W movq rax,rcx +0x3ed39fc0d6f5 b5 ebf6 jmp 0x3ed39fc0d6ed <+0xad> +0x3ed39fc0d6f7 b7 e884fbffff call 0x3ed39fc0d280 ;; wasm stub: WasmStackGuard +0x3ed39fc0d6fc bc 488b45e8 REX.W movq rax,[rbp-0x18] +0x3ed39fc0d700 c0 488b75f0 REX.W movq rsi,[rbp-0x10] +0x3ed39fc0d704 c4 e953ffffff jmp 0x3ed39fc0d65c <+0x1c> +0x3ed39fc0d709 c9 48894de0 REX.W movq [rbp-0x20],rcx +0x3ed39fc0d70d cd 48897dd8 REX.W movq [rbp-0x28],rdi +0x3ed39fc0d711 d1 e86afbffff call 0x3ed39fc0d280 ;; wasm stub: WasmStackGuard +0x3ed39fc0d716 d6 488b45e8 REX.W movq rax,[rbp-0x18] +0x3ed39fc0d71a da 488b4de0 REX.W movq rcx,[rbp-0x20] +0x3ed39fc0d71e de 488b7dd8 REX.W movq rdi,[rbp-0x28] +0x3ed39fc0d722 e2 488b75f0 REX.W movq rsi,[rbp-0x10] +0x3ed39fc0d726 e6 e962ffffff jmp 0x3ed39fc0d68d <+0x4d> +0x3ed39fc0d72b eb 90 nop +``` + +As you can see the call to `foo()` (`call r9`) is in the middle of the loop code. + +Is there a way to produce different Wasm code which will result in the call being after +the rest of the loop code? + +No, because: + +- the call is part of the loop +- Wasm loops can only have one entry point + +Therefore it is impossible in Wasm to have some code part of a loop moved after it, +because it would cause irreducible control flow. + +Of course we could introduce irreducible control flow and then turn it into reducible control flow +again (Wasm compilers already must be able to do this), but there are only two way of +fixing the irreducible control flow: duplicating the code or introducing a new variable +to direct the control flow: + +- The first alternative is not feasible in the general case because the code duplication +can incur in exponential blowup + +- The second is feasible but introduces so much overhead that it defeats the purpose + +# Branch Hinting proposal + +The underlying problem is that there is a gap of information between the high level source code +and the final machine code that the engine runs. + +The Branch Hinting proposal aims to close the gap by explicitly passing on the missing information +to the engine. + +As a matter of fact, V8 already has the concept of "branch hinting": It is used internally +to produce better code in the presence of unlikely conditions. + +It is also currently used to implement the experimental `br_on_null` instruction (with the assumption +that it is unlikely to be null). + +These of course are implementation details, but they hint at the fact that this functionality +is valuable. + +Without this proposal, performance sesitive applications that benefits from branch hinting +may have to rely on internal engine behavior to achieve maximum performance. + +For example the CheerpX virtual machine is currently experimenting with using br_on_null to take advantage of internal branch hinting in V8. +Even though this solution is inefficient the performance gains are nonetheless significant. +Proper support for branch hinting would improve the performance even more. + +# Wasm code generation with branch hinting + +The V8 POC with branch hinting support produces the following machine code for our example code: + +``` +0xfe77b01d640 0 55 push rbp +0xfe77b01d641 1 4889e5 REX.W movq rbp,rsp +0xfe77b01d644 4 6a0a push 0xa +0xfe77b01d646 6 56 push rsi +0xfe77b01d647 7 4883ec20 REX.W subq rsp,0x20 +0xfe77b01d64b b 488b5e23 REX.W movq rbx,[rsi+0x23] +0xfe77b01d64f f 483b23 REX.W cmpq rsp,[rbx] +0xfe77b01d652 12 0f8656000000 jna 0xfe77b01d6ae <+0x6e> +0xfe77b01d658 18 83f800 cmpl rax,0x0 +0xfe77b01d65b 1b 0f95c3 setnzl bl +0xfe77b01d65e 1e 0fb6db movzxbl rbx,rbx +0xfe77b01d661 21 33c9 xorl rcx,rcx +0xfe77b01d663 23 488bd1 REX.W movq rdx,rcx +0xfe77b01d666 26 488bf9 REX.W movq rdi,rcx +0xfe77b01d669 29 0f1f8000000000 nop +0xfe77b01d670 30 4c8b4623 REX.W movq r8,[rsi+0x23] +0xfe77b01d674 34 493b20 REX.W cmpq rsp,[r8] +0xfe77b01d677 37 0f8644000000 jna 0xfe77b01d6c1 <+0x81> +0xfe77b01d67d 3d 83fb00 cmpl rbx,0x0 +0xfe77b01d680 40 0f841b000000 jz 0xfe77b01d6a1 <+0x61> +0xfe77b01d686 46 448d40ff leal r8,[rax-0x1] +0xfe77b01d68a 4a 443bc7 cmpl r8,rdi +0xfe77b01d68d 4d 0f845b000000 jz 0xfe77b01d6ee <+0xae> +0xfe77b01d693 53 83c701 addl rdi,0x1 +0xfe77b01d696 56 03cf addl rcx,rdi +0xfe77b01d698 58 3bf8 cmpl rdi,rax +0xfe77b01d69a 5a 7cd4 jl 0xfe77b01d670 <+0x30> +0xfe77b01d69c 5c e908000000 jmp 0xfe77b01d6a9 <+0x69> +0xfe77b01d6a1 61 488bc2 REX.W movq rax,rdx +0xfe77b01d6a4 64 488be5 REX.W movq rsp,rbp +0xfe77b01d6a7 67 5d pop rbp +0xfe77b01d6a8 68 c3 retl +0xfe77b01d6a9 69 488bc1 REX.W movq rax,rcx +0xfe77b01d6ac 6c ebf6 jmp 0xfe77b01d6a4 <+0x64> +0xfe77b01d6ae 6e 488945e8 REX.W movq [rbp-0x18],rax +0xfe77b01d6b2 72 e8c9fbffff call 0xfe77b01d280 ;; wasm stub: WasmStackGuard +0xfe77b01d6b7 77 488b45e8 REX.W movq rax,[rbp-0x18] +0xfe77b01d6bb 7b 488b75f0 REX.W movq rsi,[rbp-0x10] +0xfe77b01d6bf 7f eb97 jmp 0xfe77b01d658 <+0x18> +0xfe77b01d6c1 81 488945e8 REX.W movq [rbp-0x18],rax +0xfe77b01d6c5 85 48894de0 REX.W movq [rbp-0x20],rcx +0xfe77b01d6c9 89 48897dd8 REX.W movq [rbp-0x28],rdi +0xfe77b01d6cd 8d 48895dd0 REX.W movq [rbp-0x30],rbx +0xfe77b01d6d1 91 e8aafbffff call 0xfe77b01d280 ;; wasm stub: WasmStackGuard +0xfe77b01d6d6 96 33d2 xorl rdx,rdx +0xfe77b01d6d8 98 488b45e8 REX.W movq rax,[rbp-0x18] +0xfe77b01d6dc 9c 488b4de0 REX.W movq rcx,[rbp-0x20] +0xfe77b01d6e0 a0 488b7dd8 REX.W movq rdi,[rbp-0x28] +0xfe77b01d6e4 a4 488b75f0 REX.W movq rsi,[rbp-0x10] +0xfe77b01d6e8 a8 488b5dd0 REX.W movq rbx,[rbp-0x30] +0xfe77b01d6ec ac eb8f jmp 0xfe77b01d67d <+0x3d> +0xfe77b01d6ee ae 448b462b movl r8,[rsi+0x2b] +0xfe77b01d6f2 b2 4d03c5 REX.W addq r8,r13 +0xfe77b01d6f5 b5 458b4007 movl r8,[r8+0x7] +0xfe77b01d6f9 b9 4d03c5 REX.W addq r8,r13 +0xfe77b01d6fc bc 4c8b4e2f REX.W movq r9,[rsi+0x2f] +0xfe77b01d700 c0 4d8b09 REX.W movq r9,[r9] +0xfe77b01d703 c3 488945e8 REX.W movq [rbp-0x18],rax +0xfe77b01d707 c7 48894de0 REX.W movq [rbp-0x20],rcx +0xfe77b01d70b cb 48897dd8 REX.W movq [rbp-0x28],rdi +0xfe77b01d70f cf 48895dd0 REX.W movq [rbp-0x30],rbx +0xfe77b01d713 d3 4c8bd6 REX.W movq r10,rsi +0xfe77b01d716 d6 498bf0 REX.W movq rsi,r8 +0xfe77b01d719 d9 4d8bc2 REX.W movq r8,r10 +0xfe77b01d71c dc 41ffd1 call r9 +0xfe77b01d71f df 33d2 xorl rdx,rdx +0xfe77b01d721 e1 488b45e8 REX.W movq rax,[rbp-0x18] +0xfe77b01d725 e5 488b4de0 REX.W movq rcx,[rbp-0x20] +0xfe77b01d729 e9 488b7dd8 REX.W movq rdi,[rbp-0x28] +0xfe77b01d72d ed 488b75f0 REX.W movq rsi,[rbp-0x10] +0xfe77b01d731 f1 488b5dd0 REX.W movq rbx,[rbp-0x30] +0xfe77b01d735 f5 e959ffffff jmp 0xfe77b01d693 <+0x53> +0xfe77b01d73a fa 90 nop +0xfe77b01d73b fb 90 nop +``` + +As you can see the call to `foo()` (`call r9`) is laid out after the rest of the loop code. + +As a result, the hinted version runs ~25% faster than the non-hinted one. + +Of course real world code is more complex and probably doing some real computation +instead of just adding numbers in a tight loop. The actual performance gain will +depend heavily on the specific code, and how good the assumptions about the likeness +of a branch are. + +Techniques like Performance Guided Optimization can also make use of this feature +and provide more precise hints. diff --git a/proposals/branch-hinting/Overview.md b/proposals/branch-hinting/Overview.md new file mode 100644 index 000000000..dedd4b4c8 --- /dev/null +++ b/proposals/branch-hinting/Overview.md @@ -0,0 +1,130 @@ +# Branch hinting + +## Introduction + +### Motivation + +The motivation for this proposal is to improve the performance of the compiled wasm +code, by informing the engine that a particular branch instruction is very likely to take +a specific path. + +This allows the engine to make better decisions for code layout (improving instruction cache hits) +and register allocation. + +See [Motivation](/proposals/branch-hinting/Motivation.md) for more information. + + +### Background + +This proposal has first been presented here: + +https://github.com/WebAssembly/design/issues/1363 + +This topic was preliminarily discussed at the CG-09-29 meeting: + +https://github.com/WebAssembly/meetings/blob/master/main/2020/CG-09-29.md + +The consensus seemed to be to proceed with the idea, but to present some benchmarks +to prove the need for the proposal. + +A [preliminary benchmark](/benchmarks) showed that the proposal is worth investigating + +The proposal was accepted for phase 1 at the CG-11-10 meeting: + +https://github.com/WebAssembly/meetings/blob/master/main/2020/CG-11-10.md + +Discussion about how to implement the proposal happened in this issue: + +https://github.com/WebAssembly/branch-hinting/issues/1 + +And some more discussion and feedback on the custom section format was done in the CG-01-05: + +https://github.com/WebAssembly/meetings/blob/master/main/2021/CG-01-05.md + +After that, a tentative spec was written and there was further discussion in CG-02-16: + +https://github.com/WebAssembly/meetings/blob/master/main/2021/CG-02-16.md + +And in CG-03-02: + +https://github.com/WebAssembly/meetings/blob/main/main/2021/CG-03-02.md + +Finally after working on the feedback received, a poll for phase 2 was made, and passed, in CG-03-16: + +https://github.com/WebAssembly/meetings/blob/main/main/2021/CG-03-16.md + +The feature landed in V8 92 under an experimental flag, and the CheerpX virtual machine started using it, +with measurable performance improvements. The results were shown in CG-06-08, plus the start of +a discussion about testing requirements for phase 3: + +https://github.com/WebAssembly/meetings/blob/main/main/2021/CG-06-08.md + +After the Instrument and Tracing Proposal passed to phase 2 in CG-07-20, the CG felt the need to +have a framework for standardizing proposals that use custom section as a way to hint the code +without altering the semantics. Ongoing discussion is in a design issue: + +https://github.com/WebAssembly/design/issues/1424 + +The result of that discussion prompted the design of Code Annotations, presented in CG-08-17: + +https://github.com/WebAssembly/meetings/blob/main/main/2021/CG-08-17.md + +and in CG-09-28: + +https://github.com/WebAssembly/meetings/blob/main/main/2021/CG-09-28.md + +Branch hinting adpoted the Code Annotation format and an update about the new format +and the current status of the proposal, as well as a poll for phase 3 (which passed) + was held on CG-11-09: + +https://github.com/WebAssembly/meetings/blob/main/main/2021/CG-11-09.md + + + +### Design + +The *branch hint section* is a **custom section** whose name string is `metadata.code.branch_hint`. +The branch hints section should appear only once in a module, and only before the code section. + +The purpose of this section is to aid the compilation of conditional branch instructions, by providing a hint that a branch is very likely (or unlikely) to be taken. + +The section contains a vector of *function branch hints* each representing the branch hints for a single function. + +Each *function branch hints* structure consists of + +* the function index of the function the hints are referring to, +* a vector of *branch hint* for the function. + +Elements of the vector of *function branch hints* must appear in increasing function index order, +and a function index can appear at most once. + +Each *branch hint* structure consists of + +* the |U32| byte offset of the hinted instruction, relative to the beginning of the function locals declaration, +* A |U32| with value `1`, +* a |U32| indicating the meaning of the hint: + +| value | meaning | +|-------|-------------------| +| 0 | likely not taken | +| 1 | likely taken | + +Elements of the vector of *branch hint* must appear in increasing byte offset order, +and a byte offset can appear at most once. A |BRIF| or |IF| instruction must be present +in the code section at the specified offset. + +#### Annotations + +*Branch Hint annotations* are the textual analogue to the branch hint section and provide a textual representation for it. +Consequently, their id is :math:`@metadata.code.branch_hint`. + +Branch hint annotations are allowed only on |BRIF| and |IF| instructions, +and at most one branch hint annotation may be given per instruction. + +Branch hint annotations have the following format: + +``` +(@metadata.code.branch_hint "\00" | "\01") +``` + +See the [the spec](/document/core/appendix/custom.rst) for the formal notation. diff --git a/proposals/bulk-memory-operations/Overview.md b/proposals/bulk-memory-operations/Overview.md index 0dfc53264..c93eaa3a0 100644 --- a/proposals/bulk-memory-operations/Overview.md +++ b/proposals/bulk-memory-operations/Overview.md @@ -228,7 +228,7 @@ The meaning of the bits of the flag field (a `varuint32`) for element segments i | 0 | 0=is active, 1=is passive | | 1 | if bit 0 clear: 0=table 0, 1=has table index | | | if bit 0 set: 0=active, 1=declared | -| 2 | 0=carries indicies; 1=carries elemexprs | +| 2 | 0=carries indices; 1=carries elemexprs | which yields this view, with the fields carried by each flag value: @@ -356,7 +356,7 @@ The bounds check is performed before any data are written. ### `table.init`, `elem.drop`, and `table.copy` instructions -The `table.*` instructions behave similary to the `memory.*` instructions, with +The `table.*` instructions behave similarly to the `memory.*` instructions, with the difference that they operate on element segments and tables, instead of data segments and memories. The offset and length operands of `table.init` and `table.copy` have element units instead of bytes as well. diff --git a/proposals/exception-handling/Exceptions.md b/proposals/exception-handling/Exceptions.md index 9a2ddfb8d..d982592ce 100644 --- a/proposals/exception-handling/Exceptions.md +++ b/proposals/exception-handling/Exceptions.md @@ -1,8 +1,8 @@ # Exception handling This explainer reflects the up-to-date version of the exception handling -proposal agreed on [the CG meeting on -09-15-2020](https://github.com/WebAssembly/meetings/blob/master/main/2020/CG-09-15.md). +proposal agreed on [Oct 2023 CG +meeting](https://github.com/WebAssembly/meetings/blob/main/main/2023/CG-10.md#exception-handling-vote-on-proposal-to-re-introduce-exnref). --- @@ -36,8 +36,8 @@ succeeding instructions to process the data. A WebAssembly exception is created when you throw it with the `throw` instruction. Thrown exceptions are handled as follows: -1. They can be caught by one of the *catch clauses* in an enclosing try - block of a function body. +1. They can be caught by one of the *catch clauses* in an enclosing try block of + a function body. 1. Throws not caught within a function body continue up the call stack, popping call frames, until an enclosing try block is found. @@ -86,21 +86,22 @@ Exception tag indices are used by: 1. The `throw` instruction which creates a WebAssembly exception with the corresponding exception tag, and then throws it. -2. Catch clauses use a tag to identify the thrown exception it - can catch. If it matches, it pushes the corresponding argument values of the - exception onto the stack. +2. Catch clauses use a tag to identify the thrown exception it can catch. If it + matches, it pushes the corresponding argument values of the exception onto + the stack. ### Exception references -When caught, an exception is reified into an _exception reference_, a value of the new type `exnref`. -Exception references can be used to rethrow the caught exception. +When caught, an exception is reified into an _exception reference_, a value of +the new type `exnref`. Exception references can be used to rethrow the caught +exception. ### Try blocks A _try block_ defines a list of instructions that may need to process exceptions and/or clean up state when an exception is thrown. Like other higher-level -constructs, a try block begins with a `try_table` instruction, and ends with an `end` -instruction. That is, a try block is sequence of instructions having the +constructs, a try block begins with a `try_table` instruction, and ends with an +`end` instruction. That is, a try block is sequence of instructions having the following form: ``` @@ -109,10 +110,11 @@ try_table blocktype catch* end ``` -A try block contains zero or more _catch clauses_. If there are no catch clauses, then the try block does not catch any exceptions. +A try block contains zero or more _catch clauses_. If there are no catch +clauses, then the try block does not catch any exceptions. -The _body_ of the try block is the list of instructions after the last -catch clause, if any. +The _body_ of the try block is the list of instructions after the last catch +clause, if any. Each `catch` clause can be in one of 4 forms: ``` @@ -121,16 +123,16 @@ catch_ref tag label catch_all label catch_all_ref label ``` -All forms have a label which is branched to when an exception is cought (see below). -The former two forms have an exception tag associated with it that -identifies what exceptions it will catch. -The latter two forms catch any exception, so that they can be used to define a _default_ handler. +All forms have a label which is branched to when an exception is caught (see +below). The former two forms have an exception tag associated with it that +identifies what exceptions it will catch. The latter two forms catch any +exception, so that they can be used to define a _default_ handler. Try blocks, like control-flow blocks, have a _block type_. The block type of a try block defines the values yielded by evaluating the try block when either no -exception is thrown, or the exception is successfully caught by the catch clause. -Because `try_table` defines a control-flow block, it can be -targets for branches (`br` and `br_if`) as well. +exception is thrown, or the exception is successfully caught by the catch +clause. Because `try_table` defines a control-flow block, it can be targets for +branches (`br` and `br_if`) as well. ### Throwing an exception @@ -160,37 +162,38 @@ Once a catching try block is found for the thrown exception, the operand stack is popped back to the size the operand stack had when the try block was entered after possible block parameters were popped. -Then catch clauses are tried in the order -they appear in the catching try block, until one matches. If a matching catch clause is found, control is transferred to the label of that catch clause. -In case of `catch` or `catch_ref`, -the arguments of the exception are pushed back onto the stack. -For `catch_ref` and `catch_all_ref`, an exception reference is then pushed to the stack, which represents the caught exception. +Then catch clauses are tried in the order they appear in the catching try block, +until one matches. If a matching catch clause is found, control is transferred +to the label of that catch clause. In case of `catch` or `catch_ref`, the +arguments of the exception are pushed back onto the stack. For `catch_ref` and +`catch_all_ref`, an exception reference is then pushed to the stack, which +represents the caught exception. If no catch clauses were matched, the exception is implicitly rethrown. -Note that a caught exception can be rethrown explicitly using the `exnref` and the `throw_ref` instruction. +Note that a caught exception can be rethrown explicitly using the `exnref` and +the `throw_ref` instruction. ### Rethrowing an exception -The `throw_ref` takes an operand of type `exnref` and re-throws the corresponding caught exception. -If the operand is null, a trap occurs. +The `throw_ref` takes an operand of type `exnref` and re-throws the +corresponding caught exception. If the operand is null, a trap occurs. ### JS API #### Traps -Catch clauses handle exceptions generated by the `throw` -instruction, but do not catch traps. The rationale for this is that in general -traps are not locally recoverable and are not needed to be handled in local -scopes like try blocks. +Catch clauses handle exceptions generated by the `throw` instruction, but do not +catch traps. The rationale for this is that in general traps are not locally +recoverable and are not needed to be handled in local scopes like try blocks. The `try_table` instruction catches foreign exceptions generated from calls to function imports as well, including JavaScript exceptions, with a few exceptions: 1. In order to be consistent before and after a trap reaches a JavaScript frame, the `try_table` instruction does not catch exceptions generated from traps. -1. The `try_table` instruction does not catch JavaScript exceptions generated from - stack overflow and out of memory. +1. The `try_table` instruction does not catch JavaScript exceptions generated + from stack overflow and out of memory. Filtering these exceptions should be based on a predicate that is not observable by JavaScript. Traps currently generate instances of @@ -232,22 +235,22 @@ check ensures that without access to a WebAssembly module's exported exception tag, the associated data fields cannot be read. The `Exception` constructor can take an optional `ExceptionOptions` argument, -which can optionally contain `traceStack` entry. When `traceStack` is -`true`, JavaScript VMs are permitted to attach a stack trace string to -`Exception.stack` field, as in JavaScript's `Error` class. `traceStack` -serves as a request to the WebAssembly engine to attach a stack trace; it -is not necessary to honour if `true`, but `trace` may not be populated if -`traceStack` is `false`. While `Exception` is not a subclass of JavaScript's -`Error` and it can be used to represent normal control flow constructs, -`traceStack` field can be set when we use it to represent errors. The -format of stack trace strings conform to the [WebAssembly stack trace +which can optionally contain `traceStack` entry. When `traceStack` is `true`, +JavaScript VMs are permitted to attach a stack trace string to `Exception.stack` +field, as in JavaScript's `Error` class. `traceStack` serves as a request to the +WebAssembly engine to attach a stack trace; it is not necessary to honour if +`true`, but `trace` may not be populated if `traceStack` is `false`. While +`Exception` is not a subclass of JavaScript's `Error` and it can be used to +represent normal control flow constructs, `traceStack` field can be set when we +use it to represent errors. The format of stack trace strings conform to the +[WebAssembly stack trace conventions](https://webassembly.github.io/spec/web-api/index.html#conventions). When `ExceptionOption` is not provided or it does not contain `traceStack` entry, `traceStack` is considered `false` by default. To preserve stack trace info when crossing the JS to Wasm boundary, exceptions -can internally contain a stack trace, which is propagated when caught by a `catch[_all]_ref` clause -and rethrown by `throw_ref`. +can internally contain a stack trace, which is propagated when caught by a +`catch[_all]_ref` clause and rethrown by `throw_ref`. More formally, the added interfaces look like the following: @@ -331,16 +334,17 @@ document](https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md). The type `exnref` is represented by the type opcode `-0x17`. -When combined with the [GC proposal](https://github.com/WebAssembly/gc/blob/main/proposals/gc/MVP.md), -there also is a value type `nullexnref` with opcode `-0x0c`. -Furthermore, these opcodes also function as heap type, -i.e., `exn` is a new heap type with opcode `-0x17`, -and `noexn` is a new heap type with opcode `-0x0c`; -`exnref` and `nullexnref` are shorthands for `(ref null exn)` and `(ref null noexn)`, respectively. +When combined with the [GC +proposal](https://github.com/WebAssembly/gc/blob/main/proposals/gc/MVP.md), +there also is a value type `nullexnref` with opcode `-0x0c`. Furthermore, these +opcodes also function as heap type, i.e., `exn` is a new heap type with opcode +`-0x17`, and `noexn` is a new heap type with opcode `-0x0c`; `exnref` and +`nullexnref` are shorthands for `(ref null exn)` and `(ref null noexn)`, +respectively. -The heap type `noexn` is a subtype of `exn`. -They are not in a subtype relation with any other type (except bottom), -such that they form a new disjoint hierarchy of heap types. +The heap type `noexn` is a subtype of `exn`. They are not in a subtype relation +with any other type (except bottom), such that they form a new disjoint +hierarchy of heap types. ##### tag_type @@ -439,13 +443,13 @@ follows: ###### Tag names -The tag names subsection is a `name_map` which assigns names to a subset of -the tag indices (Used for both imports and module-defined). +The tag names subsection is a `name_map` which assigns names to a subset of the +tag indices (Used for both imports and module-defined). ### Control flow instructions -The control flow instructions are extended to define try blocks and -throws as follows: +The control flow instructions are extended to define try blocks and throws as +follows: | Name | Opcode | Immediates | Description | | ---- | ---- | ---- | ---- | diff --git a/proposals/exception-handling/Exceptions-formal-examples.md b/proposals/exception-handling/legacy/Exceptions-formal-examples.md similarity index 100% rename from proposals/exception-handling/Exceptions-formal-examples.md rename to proposals/exception-handling/legacy/Exceptions-formal-examples.md diff --git a/proposals/exception-handling/Exceptions-formal-overview.md b/proposals/exception-handling/legacy/Exceptions-formal-overview.md similarity index 100% rename from proposals/exception-handling/Exceptions-formal-overview.md rename to proposals/exception-handling/legacy/Exceptions-formal-overview.md diff --git a/proposals/exception-handling/legacy/Exceptions.md b/proposals/exception-handling/legacy/Exceptions.md new file mode 100644 index 000000000..74270f88c --- /dev/null +++ b/proposals/exception-handling/legacy/Exceptions.md @@ -0,0 +1,711 @@ +# Exception handling + +This explainer reflects the third version of the proposal adopted in the [CG +meeting on +09-15-2020](https://github.com/WebAssembly/meetings/blob/main/main/2020/CG-09-15.md#proposal-for-changes-in-eh-proposal-for-two-phase-unwinding-support-part-2--discussions-heejin-ahn-30-min), +which removed `exnref`. The rational then was the old proposal having a +first-class exception reference type was not easily extensible to a future +proposal that supports two-phase unwinding. + +This proposal was superseded by the [current +proposal](https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md) +in [Oct 2023 CG +meeting](https://github.com/WebAssembly/meetings/blob/main/main/2023/CG-10.md#exception-handling-vote-on-proposal-to-re-introduce-exnref), +recognizing that after all `exnref` provided important functionalities for the +spec and engine/toolchain implementations. + +This proposal is currently also known as the "legacy proposal" and still +supported in the web, but can be deprecated in future and the use of this +proposal is discouraged. + +--- + +## Overview + +Exception handling allows code to break control flow when an exception is +thrown. The exception can be any exception known by the WebAssembly module, or +it may an unknown exception that was thrown by a called imported function. + +One of the problems with exception handling is that both WebAssembly and an +embedder have different notions of what exceptions are, but both must be aware +of the other. + +It is difficult to define exceptions in WebAssembly because (in general) it +doesn't have knowledge of any embedder. Further, adding such knowledge to +WebAssembly would limit the ability for other embedders to support WebAssembly +exceptions. + +One issue is that both sides need to know if an exception was thrown by the +other, because cleanup may need to be performed. + +Another problem is that WebAssembly doesn't have direct access to an embedder's +memory. As a result, WebAssembly defers the handling of exceptions to the host +VM. + +To access exceptions, WebAssembly provides instructions to check if the +exception is one that WebAssembly understands. If so, the data of the +WebAssembly exception is extracted and copied onto the stack, allowing +succeeding instructions to process the data. + +A WebAssembly exception is created when you throw it with the `throw` +instruction. Thrown exceptions are handled as follows: + +1. They can be caught by one of `catch`/`catch_all` blocks in an enclosing try + block of a function body. + +1. Throws not caught within a function body continue up the call stack, popping + call frames, until an enclosing try block is found. + +1. If the call stack is exhausted without any enclosing try blocks, the embedder + defines how to handle the uncaught exception. + +### Exception handling + +This proposal adds exception handling to WebAssembly. Part of this proposal is +to define a new section to declare exceptions. However, rather than limiting +this new section to just defining exceptions, it defines a more general format +`tag` that allows the declaration of other forms of typed tags in future. + +WebAssembly tags are defined in a new `tag` section of a WebAssembly module. The +tag section is a list of declared tags that are created fresh each time the +module is instantiated. + +Each tag has an `attribute` and a `type`. Currently, the attribute can only +specify that the tag is for an exception. In the future, additional attribute +values may be added when other kinds of tags are added to WebAssembly. + +To allow for such a future extension possibility, we reserve a byte in the +binary format of an exception definition, set to 0 to denote an exception +attribute. + +### Exceptions + +An `exception tag` is a value to distinguish different exceptions, while an +`exception tag index` is a numeric name to refer to an (imported or defined) +exception tag within a module (see [tag index space](#tag-index-space) for +details). Exception tags are declared in the tag and import sections of a +module. + +An `exception` is an internal construct in WebAssembly that represents a runtime +object that can be thrown. A WebAssembly exception consists of an exception tag +and its runtime arguments. + +The type of an exception tag is denoted by an index to a function signature +defined in the `type` section. The parameters of the function signature define +the list of argument values associated with the tag. The result type must be +empty. + +Exception tag indices are used by: + +1. The `throw` instruction which creates a WebAssembly exception with the + corresponding exception tag, and then throws it. + +2. The `catch` clause uses the tag to identify if the thrown exception is one it + can catch. If true it pushes the corresponding argument values of the + exception onto the stack. + +### Try-catch blocks + +A _try block_ defines a list of instructions that may need to process exceptions +and/or clean up state when an exception is thrown. Like other higher-level +constructs, a try block begins with a `try` instruction, and ends with an `end` +instruction. That is, a try-catch block is sequence of instructions having the +following form: + +``` +try blocktype + instruction* +catch i + instruction* +catch j + instruction* +... +catch_all + instruction* +end +``` + +A try-catch block contains zero or more `catch` blocks and zero or one +`catch_all` block. All `catch` blocks must precede the `catch_all` block, if +any. The `catch`/`catch_all` instructions (within the try construct) are called +the _catching_ instructions. There may not be any `catch` or `catch_all` blocks +after a `try`, in which case the `try` block does not catch any exceptions. + +The _body_ of the try block is the list of instructions before the first +catching instruction. The _body_ of each catch block is the sequence of +instructions following the corresponding catching instruction before the next +catching instruction (or the `end` instruction if it is the last catching +block). + +The `catch` instruction has an exception tag associated with it. The tag +identifies what exceptions it can catch. That is, any exception created with the +corresponding exception tag. Catch blocks that begin with a `catch` instruction +are considered _tagged_ catch blocks. + +The last catching instruction of a try-catch block can be the `catch_all` +instruction. If it begins with the `catch_all` instruction, it defines the +_default_ catch block. The default catch block has no tag index, and is used to +catch all exceptions not caught by any of the tagged catch blocks. The term +'catch block' refers to both `catch` and `catch_all` blocks. + +When the program runs `br` within `catch` or `catch_all` blocks, the rest of +the catching blocks will not run and the program control will branch to the +destination, as in normal blocks. + +Try blocks, like control-flow blocks, have a _block type_. The block type of a +try block defines the values yielded by evaluating the try block when either no +exception is thrown, or the exception is successfully caught by the catch block. +Because `try` and `end` instructions define a control-flow block, they can be +targets for branches (`br` and `br_if`) as well. + +### Throwing an exception + +The `throw` instruction takes an exception tag index as an immediate argument. +That index is used to identify the exception tag to use to create and throw the +corresponding exception. + +The values on top of the stack must correspond to the type associated with the +exception tag. These values are popped off the stack and are used (along with +the corresponding exception tag) to create the corresponding exception. That +exception is then thrown. + +When an exception is thrown, the embedder searches for the nearest enclosing try +block body that execution is in. That try block is called the _catching_ try +block. + +If the throw appears within the body of a try block, it is the catching try +block. + +If a throw occurs within a function body, and it doesn't appear inside the body +of a try block, the throw continues up the call stack until it is in the body of +an an enclosing try block, or the call stack is flushed. If the call stack is +flushed, the embedder defines how to handle uncaught exceptions. Otherwise, the +found enclosing try block is the catching try block. + +A throw inside the body of a catch block is never caught by the corresponding +try block of the catch block, since instructions in the body of the catch block +are not in the body of the try block. + +Once a catching try block is found for the thrown exception, the operand stack +is popped back to the size the operand stack had when the try block was entered +after possible block parameters were popped. + +Then in case of a try-catch block, tagged catch blocks are tried in the order +they appear in the catching try block, until one matches. If a matched tagged +catch block is found, control is transferred to the body of the catch block, and +the arguments of the exception are pushed back onto the stack. + +Otherwise, control is transferred to the body of the `catch_all` block, if any. +However, unlike tagged catch blocks, the constructor arguments are not copied +back onto the operand stack. + +If no tagged catch blocks were matched, and the catching try block doesn't have +a `catch_all` block, the exception is rethrown. + +If control is transferred to the body of a catch block, and the last instruction +in the body is executed, control then exits the try block. + +If the selected catch block does not throw an exception, it must yield the +value(s) specified by the type annotation on the corresponding catching try +block. + +Note that a caught exception can be rethrown using the `rethrow` instruction. + +### Rethrowing an exception + +The `rethrow` instruction can only appear in the body of a catch/catch_all +block. It always re-throws the exception caught by an enclosing catch block. + +Associated with the `rethrow` instruction is a _label_. The label is used to +disambiguate which exception is to be rethrown, when inside nested catch blocks. +The label is the relative block depth to the corresponding try block for which +the catching block appears. + +For example consider the following: + +``` +try $l1 + ... +catch ;; $l1 + ... + block + ... + try $l2 + ... + catch ;; $l2 + ... + try $l3 + ... + catch ;; $l3 + ... + rethrow N ;; (or label) + end + end + end + ... +end +``` + +In this example, `N` is used to disambiguate which caught exception is being +rethrown. It could rethrow any of the three caught expceptions. Hence, `rethrow +0` corresponds to the exception caught by `catch 3`, `rethrow 1` corresponds to +the exception caught by `catch 2`, and `rethrow 3` corresponds to the exception +caught by `catch 1`. In wat format, the argument for the `rethrow` instructions +can also be written as a label, like branches. So `rethrow 0` in the example +above can also be written as `rethrow $l3`. + +Note that `rethrow 2` is not allowed because it does not refer to a `try` label +from within its catch block. Rather, it references a `block` instruction, so it +will result in a validation failure. + +Note that the example below is a validation failure: +``` +try $l1 + try $l2 + rethrow $l2 ;; (= rethrow 0) + catch + end +catch +end +``` +The `rethrow` here references `try $l2`, but the `rethrow` is not within its +`catch` block. + +The example below includes all of the cases explained above. The numbers +within `()` after `rethrow`s are the label operands in immediate values. +``` +(func $test + try $lA + ... + catch ($lA) + ... + block $lB + ... + try $lC + ... + catch ($lC) + ... + try $lD + ... + rethrow $lD (0) ;; refers to 'catch ($lD)', but it is not within 'catch ($lD)', so validation failure + rethrow $lC (1) ;; rethrows the exception caught by catch ($lC) + rethrow $lB (2) ;; refers to 'block $lB', so validation failure + rethrow $lA (3) ;; rethrows the exception caught by catch ($lA) + rethrow 4 ;; validation failure + catch ($lD) + ... + rethrow $lD (0) ;; rethrows the exception caught by catch ($lD) + rethrow $lC (1) ;; rethrows the exception caught by catch ($lC) + rethrow $lB (2) ;; refers to 'block $lB', so validation failure + rethrow $lA (3) ;; rethrows the exception caught by catch ($lA) + rethrow 4 ;; validation failure + end + end + end + ... + end +``` + +### Try-delegate blocks + +Try blocks can also be used with the `delegate` instruction. A try-delegate +block contains a `delegate` instruction with the following form: + +``` +try blocktype + instruction* +delegate label +``` + +The `delegate` clause does not have an associated body, so try-delegate blocks +don't have an `end` instruction at the end. The `delegate` instruction takes a +try label and delegates exception handling to a `catch`/`catch_all`/`delegate` +specified by the `try` label. For example, consider this code: + +``` +try $l0 + try + call $foo + delegate $l0 ;; (= delegate 0) +catch + ... +catch_all + ... +end +``` + +If `call $foo` throws, searching for a catching block first finds `delegate`, +and because it delegates exception handling to catching instructions associated +with `$l1`, it will be next checked by the outer `catch` and then `catch_all` +instructions. + +`delegate` can also target `catch`-less `try`s or non-`try` block constructs +like `block`s or `loop`s, in which case the delegated exception is assumed to +propagate to the outer scope and will be caught by the next matching +try-catches, or rethrown to the caller if there is no outer try block. In the +examples, catches are annotated with `($label_name)` to clarify which `try` it +belongs to for clarification; it is not the official syntax. +``` +try $l0 + block $l1 + try + call $foo + delegate $l1 ;; delegates to 'catch ($l0)' + end +catch ($l0) +end +``` + +Like branches, `delegate` can only target outer blocks, and effectively +rethrows the exception in that block. Consequently, delegating to a specific +`catch` or `catch_all` handler requires targeting the respective label from +within the associated `try` block. Delegating to a label from within a `catch` +block does delegate the exception to the next enclosing handler -- analogous to +performing a `throw` within a `catch` block, that handler is no longer active +at that point. Here is another example: + +``` +try $l0 + try $l1 + catch ($l1) + try + call $foo + delegate $l1 ;; delegates to 'catch ($l0)' + catch_all + ... + end +catch ($l0) +``` + +Here the `delegate` is targeting `catch ($l1)`, which exists before the +`delegate`. So in case an exception occurs, it propagates out and ends up +targetting `catch ($l0)`, if the catch has a matching tag. If not, it will +propagate further out. Even if the `catch_all` is below the `delegate`, +`delegate` targets catches of a `try` as a whole and does not target an +individual `catch`/`catch_all`, so it doesn't apply. + +If `delegate` targets the implicit function body block, then in effect it +delegates the exception to the caller of the current function. For example: +``` +(func $test + try + try + call $foo + delegate 1 ;; delegates to the caller + catch + ... + catch_all + ... + end +) +``` +In case `foo` throws, `delegate 1` here delegates the exception handling to the +caller, i.e., the exception escapes the current function. If the immediate is +greater than or equal to the number of block nesting including the implicit +function-level block, it is a validation failure. In this example, any number +equal to or greater than 2 is not allowed. + +The below is an example that includes all the cases explained. The numbers +within `()` after `delegate`s are the label operands in immediate values. +``` +(func $test + try $lA + block $lB + try $lC + try + delegate $lC (0) ;; delegates to 'catch ($lC)' + try + delegate $lB (1) ;; $lB is a block, so delegates to 'catch ($lA)' + try + delegate $lA (2) ;; delegates to 'catch ($lA)' + try + delegate 3 ;; propagates to the caller + try + delegate 4 ;; validation failure + catch ($lC) + try + delegate $lC (0) ;; 'catch ($lC)' is above this instruction, + ;; so delegates to 'catch ($lA)' + try + delegate $lB (1) ;; $lB is a block, so delegates to 'catch ($lA)' + try + delegate $lA (2) ;; delegates to 'catch ($lA)' + try + delegate 3 ;; propagates to the caller + try + delegate 4 ;; validation failure + end ;; try $lC + end ;; block $lB + catch ($lA) + end ;; try $lA +) +``` + +### JS API + +#### Traps + +The `catch`/`catch_all` instruction catches exceptions generated by the `throw` +instruction, but does not catch traps. The rationale for this is that in general +traps are not locally recoverable and are not needed to be handled in local +scopes like try-catch. + +The `catch` instruction catches foreign exceptions generated from calls to +function imports as well, including JavaScript exceptions, with a few +exceptions: +1. In order to be consistent before and after a trap reaches a JavaScript frame, + the `catch` instruction does not catch exceptions generated from traps. +1. The `catch` instruction does not catch JavaScript exceptions generated from + stack overflow and out of memory. + +Filtering these exceptions should be based on a predicate that is not observable +by JavaScript. Traps currently generate instances of +[`WebAssembly.RuntimeError`](https://webassembly.github.io/reference-types/js-api/#exceptiondef-runtimeerror), +but this detail is not used to decide type. Implementations are supposed to +specially mark non-catchable exceptions. +([`instanceof`](https://tc39.es/ecma262/#sec-instanceofoperator) predicate can +be intercepted in JS, and types of exceptions generated from stack overflow and +out of memory are implementation-defined.) + +#### API additions + +The following additional classes are added to the JS API in order to allow +JavaScript to interact with WebAssembly exceptions: + + * `WebAssembly.Tag` + * `WebAssembly.Exception` + +The `WebAssembly.Tag` class represents a typed tag defined in the tag section +and exported from a WebAssembly module. It allows querying the type of a tag +following the [JS type reflection +proposal](https://github.com/WebAssembly/js-types/blob/master/proposals/js-types/Overview.md). +Constructing an instance of `Tag` creates a fresh tag, and the new tag can be +passed to a WebAssembly module as a tag import. + +In the future, `WebAssembly.Tag` may be used for other proposals that require a +typed tag and its constructor may be extended to accept other types and/or a tag +attribute to differentiate them from tags used for exceptions. + +The `WebAssembly.Exception` class represents an exception thrown from +WebAssembly, or an exception that is constructed in JavaScript and is to be +thrown to a WebAssembly exception handler. The `Exception` constructor accepts a +`Tag` argument and a sequence of arguments for the exception's data fields. The +`Tag` argument determines the exception tag to use. The data field arguments +must match the types specified by the `Tag`'s type. The `is` method can be used +to query if the `Exception` matches a given tag. The `getArg` method allows +access to the data fields of a `Exception` if a matching tag is given. This last +check ensures that without access to a WebAssembly module's exported exception +tag, the associated data fields cannot be read. + +The `Exception` constructor can take an optional `ExceptionOptions` argument, +which can optionally contain `traceStack` entry. When `traceStack` is +`true`, JavaScript VMs are permitted to attach a stack trace string to +`Exception.stack` field, as in JavaScript's `Error` class. `traceStack` +serves as a request to the WebAssembly engine to attach a stack trace; it +is not necessary to honour if `true`, but `trace` may not be populated if +`traceStack` is `false`. While `Exception` is not a subclass of JavaScript's +`Error` and it can be used to represent normal control flow constructs, +`traceStack` field can be set when we use it to represent errors. The +format of stack trace strings conform to the [WebAssembly stack trace +conventions](https://webassembly.github.io/spec/web-api/index.html#conventions). +When `ExceptionOption` is not provided or it does not contain `traceStack` +entry, `traceStack` is considered `false` by default. + +To preserve stack trace info when crossing the JS to Wasm boundary, exceptions +can internally contain a stack trace, which is propagated when caught by `catch` +and rethrown by `rethrow`. + +More formally, the added interfaces look like the following: + +```WebIDL +dictionary TagType { + required sequence parameters; +}; + +[LegacyNamespace=WebAssembly, Exposed=(Window,Worker,Worklet)] +interface Tag { + constructor(TagType type); + TagType type(); +}; + +dictionary ExceptionOptions { + boolean traceStack = false; +}; + +[LegacyNamespace=WebAssembly, Exposed=(Window,Worker,Worklet)] +interface Exception { + constructor(Tag tag, sequence payload, optional ExceptionOptions options); + any getArg(Tag tag, unsigned long index); + boolean is(Tag tag); + readonly attribute (DOMString or undefined) stack; +}; +``` + +`TagType` corresponds to a `FunctionType` in [the type reflection +proposal](https://github.com/WebAssembly/js-types/blob/main/proposals/js-types/Overview.md), +without a `results` property). `TagType` could be extended in the future for +other proposals that require a richer type specification. + +## Changes to the text format + +This section describes change in the [instruction syntax +document](https://github.com/WebAssembly/spec/blob/master/document/core/text/instructions.rst). + +### New instructions + +The following rules are added to *instructions*: + +``` + try blocktype instruction* (catch tag_index instruction*)* (catch_all instruction*)? end | + try blocktype instruction* delegate label | + throw tag_index argument* | + rethrow label | +``` + +Like the `block`, `loop`, and `if` instructions, the `try` instruction is +*structured* control flow instruction, and can be labeled. This allows branch +instructions to exit try blocks. + +The `tag_index` of the `throw` and `catch` instructions denotes the exception +tag to use when creating/extract from an exception. See [tag index +space](#tag-index-space) for further clarification of exception tags. + +## Changes to Modules document + +This section describes change in the [Modules +document](https://github.com/WebAssembly/design/blob/master/Modules.md). + +### Tag index space + +The `tag index space` indexes all imported and internally-defined exception +tags, assigning monotonically-increasing indices based on the order defined in +the import and tag sections. Thus, the index space starts at zero with imported +tags, followed by internally-defined tags in the [tag section](#tag-section). + +For tag indices that are not imported/exported, the corresponding exception tag +is guaranteed to be unique over all loaded modules. Exceptions that are imported +or exported alias the respective exceptions defined elsewhere, and use the same +tag. + +## Changes to the binary model + +This section describes changes in the [binary encoding design +document](https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md). + +#### Other Types + +##### tag_type + +We reserve a bit to denote the exception attribute: + +| Name | Value | +|-----------|-------| +| Exception | 0 | + +Each tag type has the fields: + +| Field | Type | Description | +|-------|------|-------------| +| `attribute` | `uint8` | The attribute of a tag. | +| `type` | `varuint32` | The type index for its corresponding type signature | + +##### external_kind + +A single-byte unsigned integer indicating the kind of definition being imported +or defined: + +* `4` indicating a `Tag` +[import](https://github.com/WebAssembly/design/blob/main/BinaryEncoding.md#import-section) or +[definition](#tag-section) + +### Module structure + +#### High-level structure + +A new `tag` section is introduced. + +##### Tag section + +The `tag` section comes after the [memory +section](https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#memory-section) +and before the [global +section](https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#global-section). +So the list of all sections will be: + +| Section Name | Code | Description | +| ------------ | ---- | ----------- | +| Type | `1` | Function signature declarations | +| Import | `2` | Import declarations | +| Function | `3` | Function declarations | +| Table | `4` | Indirect function table and other tables | +| Memory | `5` | Memory attributes | +| Tag | `13` | Tag declarations | +| Global | `6` | Global declarations | +| Export | `7` | Exports | +| Start | `8` | Start function declaration | +| Element | `9` | Elements section | +| Data count | `12` | Data count section | +| Code | `10` | Function bodies (code) | +| Data | `11` | Data segments | + +The tag section declares a list of tag types as follows: + +| Field | Type | Description | +|-------|------|-------------| +| count | `varuint32` | count of the number of tags to follow | +| type | `tag_type*` | The definitions of the tag types | + +##### Import section + +The import section is extended to include tag definitions by extending an +`import_entry` as follows: + +If the `kind` is `Tag`: + +| Field | Type | Description | +|-------|------|-------------| +| `type` | `tag_type` | the tag being imported | + +##### Export section + +The export section is extended to reference tag types by extending an +`export_entry` as follows: + +If the `kind` is `Tag`: + +| Field | Type | Description | +|-------|------|-------------| +| `index` | `varuint32` | the index into the corresponding tag index space | + +##### Name section + +The set of known values for `name_type` of a name section is extended as +follows: + +| Name Type | Code | Description | +| --------- | ---- | ----------- | +| [Function](#function-names) | `1` | Assigns names to functions | +| [Local](#local-names) | `2` | Assigns names to locals in functions | +| [Tag](#tag-names) | `11` | Assigns names to tags | + +###### Tag names + +The tag names subsection is a `name_map` which assigns names to a subset of +the tag indices (Used for both imports and module-defined). + +### Control flow operators + +The control flow operators are extended to define try blocks, catch blocks, +throws, and rethrows as follows: + +| Name | Opcode | Immediates | Description | +| ---- | ---- | ---- | ---- | +| `try` | `0x06` | sig : `blocktype` | begins a block which can handle thrown exceptions | +| `catch` | `0x07` | index : `varint32` | begins the catch block of the try block | +| `catch_all` | `0x19` | | begins the catch_all block of the try block | +| `delegate` | `0x18` | relative_depth : `varuint32` | begins the delegate block of the try block | +| `throw` | `0x08` | index : `varint32` | Creates an exception defined by the tag and then throws it | +| `rethrow` | `0x09` | relative_depth : `varuint32` | Pops the `exnref` on top of the stack and throws it | + +The *sig* fields of `block`, `if`, and `try` operators are block signatures +which describe their use of the operand stack. diff --git a/proposals/exception-handling/old/Exceptions-v1.md b/proposals/exception-handling/pre-legacy/Exceptions-v1.md similarity index 98% rename from proposals/exception-handling/old/Exceptions-v1.md rename to proposals/exception-handling/pre-legacy/Exceptions-v1.md index a5ca40df8..c48fa64ed 100644 --- a/proposals/exception-handling/old/Exceptions-v1.md +++ b/proposals/exception-handling/pre-legacy/Exceptions-v1.md @@ -1,10 +1,8 @@ -THIS DOCUMENT IS OBSOLETE! +This is the first version of the exception handling proposal, active from 2017 +to 2018 and superseded by [V2 proposal](Exceptions-v2.md) in [Oct 2018 CG +meeting](https://github.com/WebAssembly/meetings/blob/main/main/2018/TPAC.md#exception-handling-ben-titzer). -Please see The new [Level 1 MVP Proposal] instead. - -[Level 1 MVP Proposal]: Exceptions-v2-Level-1.md - -The original proposal is preserved here for reference. +--- # Exception handling diff --git a/proposals/exception-handling/old/Exceptions-v2-Level-1+N.md b/proposals/exception-handling/pre-legacy/Exceptions-v2-Level-1+N.md similarity index 97% rename from proposals/exception-handling/old/Exceptions-v2-Level-1+N.md rename to proposals/exception-handling/pre-legacy/Exceptions-v2-Level-1+N.md index 7372ef029..08a1a5e0e 100644 --- a/proposals/exception-handling/old/Exceptions-v2-Level-1+N.md +++ b/proposals/exception-handling/pre-legacy/Exceptions-v2-Level-1+N.md @@ -1,3 +1,8 @@ +This was written along with [Level 1 Proposal](Exceptions-v2-Level-1.md) in the +beginning of 2018 for collecting ideas for future work. + +--- + # Exception Handling Level 1+N Extensions While the Level 1 Proposal establishes the minimum viable product (MVP) for diff --git a/proposals/exception-handling/old/Exceptions-v2-Level-1.md b/proposals/exception-handling/pre-legacy/Exceptions-v2-Level-1.md similarity index 97% rename from proposals/exception-handling/old/Exceptions-v2-Level-1.md rename to proposals/exception-handling/pre-legacy/Exceptions-v2-Level-1.md index 8c9ce8eda..32400e9a9 100644 --- a/proposals/exception-handling/old/Exceptions-v2-Level-1.md +++ b/proposals/exception-handling/pre-legacy/Exceptions-v2-Level-1.md @@ -1,3 +1,11 @@ +This is written as an alternative to [V1 Proposal](Exceptions-v1.md) in the +beginning of 2018 as the need for the first-class exception reference type was +suggested. This was later slightly modified and became the basis of [V2 +Proposal](Exceptions-v2.md), which replaced the V1 Proposal in [Oct 2018 CG +meeting](https://github.com/WebAssembly/meetings/blob/main/main/2018/TPAC.md#exception-handling-ben-titzer). + +--- + # Level 1 exception handling Level 1 of exception handling is the MVP (minimal viable proposal) for @@ -10,10 +18,6 @@ levels, and either: 2. Allow performance improvements in the VM. 3. Introduce additional new functionality not available in Level 1. -This document supersedes the original [Exceptions Proposal]. - -[Exceptions Proposal]: Exceptions-v1.md - ## Overview Exception handling allows code to break control flow when an exception is diff --git a/proposals/exception-handling/old/Exceptions-v2.md b/proposals/exception-handling/pre-legacy/Exceptions-v2.md similarity index 96% rename from proposals/exception-handling/old/Exceptions-v2.md rename to proposals/exception-handling/pre-legacy/Exceptions-v2.md index 233f28529..4ab9eb5f6 100644 --- a/proposals/exception-handling/old/Exceptions-v2.md +++ b/proposals/exception-handling/pre-legacy/Exceptions-v2.md @@ -1,11 +1,16 @@ -# Exception handling +This V2 proposal was developed from [Level 1 Proposal](Exceptions-v2-Level-1.md) +and superseded [V1 proposal](Exceptions-v1.md). We decided to adopt this +proposal in [Oct 2018 CG +meeting](https://github.com/WebAssembly/meetings/blob/main/main/2018/TPAC.md#exception-handling-ben-titzer), +recognizing the need for a first-class exception type, based on the reasoning +that it is more expressive and also more extendible to other kinds of events. + +This proposal was active from Oct 2018 to Sep 2020 and superseded by [V3 +proposal](https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/legacy/Exceptions.md). -There were two alternative proposals ([1st](Exceptions-v1.md) and -[2nd](Exceptions-v2-Level-1.md)) for the design of exception handling and we -[decided](https://github.com/WebAssembly/meetings/blob/master/2018/TPAC.md#exception-handling-ben-titzer) -on the second proposal, which uses first-class exception types, mainly based on -the reasoning that it is more expressive and also more extendible to other kinds -of events. +--- + +# Exception handling This proposal requires the following proposals as prerequisites. diff --git a/proposals/function-references/Overview.md b/proposals/function-references/Overview.md index da2b27ab5..0a58dd2c1 100644 --- a/proposals/function-references/Overview.md +++ b/proposals/function-references/Overview.md @@ -46,6 +46,7 @@ Typed references have no canonical default value, because they cannot be null. T The function `$hof` takes a function pointer as parameter, and is invoked by `$caller`, passing `$inc` as argument: ```wasm (type $i32-i32 (func (param i32) (result i32))) +(elem declare funcref (ref.func $inc)) (func $hof (param $f (ref $i32-i32)) (result i32) (i32.add (i32.const 10) (call_ref $i32-i32 (i32.const 42) (local.get $f))) diff --git a/proposals/gc/MVP.md b/proposals/gc/MVP.md index 081dfc539..cec9ee12b 100644 --- a/proposals/gc/MVP.md +++ b/proposals/gc/MVP.md @@ -466,7 +466,7 @@ Then, `$rttA` would carry supertype vector `[$rttA]`, `$rttB` has `[$rttA, $rttB Now consider a function that casts a `$B` to a `$C`: ``` (func $castBtoC (param $x (ref $B)) (result (ref $C)) - (ref.cast $C (local.get $x)) + (ref.cast (ref $C) (local.get $x)) ) ``` This can compile to machine code that (1) reads the RTT from `$x`, (2) checks that the length of its supertype table is >= 3, and (3) pointer-compares table[2] against `$rttC`. diff --git a/proposals/gc/Overview.md b/proposals/gc/Overview.md index 210c4e53b..c4982342b 100644 --- a/proposals/gc/Overview.md +++ b/proposals/gc/Overview.md @@ -437,8 +437,8 @@ For now, we assume that all array types have a ([flexible](#flexible-aggregates) Elements are accessed with generic load/store instructions that take a reference to an array: ``` (func $f (param $v (ref $vector)) - (array.get $vector (local.get $v) (i32.const 1) - (array.set $vector (local.get $v) (i32.const 2)) + (array.set $vector (local.get $v) (i32.const 1) + (array.get $vector (local.get $v) (i32.const 2)) ) ) ``` @@ -717,7 +717,7 @@ Another alternative would be a three-point mutability lattice with readonly as a The Wasm type system is intentionally simple. That implies that it cannot be expressive enough to track all type information that is available in a source program. -To allow producers to work around the inevitable limitations of the type system, down casts have to provided as an "escape hatch". +To allow producers to work around the inevitable limitations of the type system, down casts have to be provided as an "escape hatch". For example, that allows the use of type `anyref` to represent reference values whose type is not locally known. When such a value is used in a context where the producer knows its real type, it can use a down cast to recover it. @@ -729,7 +729,7 @@ This instruction checks whether the runtime type stored in `` is a runt In order to cast down the type of a struct or array, the aggregate itself must be equipped with a suitable RTT. Attaching runtime type information to aggregates happens at allocation time. A runtime type is an expression of type `rtt `, which is another form of opaque value type. It represents the static type `` at runtime. -In its plain form, a runtime type is obtained using the instruction `rtt.get` +In its plain form, a runtime type is obtained using the instruction `rtt.canon` ``` (rtt.canon ) ``` @@ -776,7 +776,7 @@ There are a number of reasons to make RTTs explicit: * It allows more choice in producers' use of RTT information, including making it optional (post-MVP), in accordance with the pay-as-you-go principle: for example, structs that are not involved in any casts do not need to pay the overhead of carrying runtime type information (depending on specifics of the GC implementation strategy). Some languages may never need to introduce any RTTs at all. -* Most importantly, making RTTs explicit separates the concerns of casting from Wasm-level polymorphism, i.e., [type parameters](Post-MVP.md#type-parameters). Type parameters can thus be treated as purely a validation artifact with no bearing on runtime. This property, known as parametricity, drastically simplifies the implementation of such type parameterisation and avoids the substantial hidden costs of reified generics that would otherwise hvae to be paid for every single use of type parameters (short of non-trivial cross-procedural dataflow analysis in the engine). +* Most importantly, making RTTs explicit separates the concerns of casting from Wasm-level polymorphism, i.e., [type parameters](Post-MVP.md#type-parameters). Type parameters can thus be treated as purely a validation artifact with no bearing on runtime. This property, known as parametricity, drastically simplifies the implementation of such type parameterisation and avoids the substantial hidden costs of reified generics that would otherwise have to be paid for every single use of type parameters (short of non-trivial cross-procedural dataflow analysis in the engine). ## Future Extensions diff --git a/proposals/memory64/Overview.md b/proposals/memory64/Overview.md new file mode 100644 index 000000000..3e947b582 --- /dev/null +++ b/proposals/memory64/Overview.md @@ -0,0 +1,402 @@ +# Memory64 + +## Summary + +This page describes a proposal to support linear memory of sizes larger than +232 bits. It provides no new instructions, but instead extends the +currently existing instructions to allow 64-bit indexes. + +In addition, in order to support source languages with 64-bit pointer width, +this proposal also extends tables to allow 64-bit indexes. This addition was +made during phase 3 of the proposal and we refer to this addition as "table64". + +### Implementation Status + +- spec interpreter: Done +- v8/chrome: [Done](https://chromium-review.googlesource.com/c/v8/v8/+/2679683) +- Firefox: Done +- Safari: ? +- wabt: [Done](https://github.com/WebAssembly/wabt/pull/1500) +- binaryen: [Done](https://github.com/WebAssembly/binaryen/pull/3202) +- emscripten: [Done](https://github.com/emscripten-core/emscripten/pull/17803) + +### Implementation Status (table64) + +- spec interpreter: [Done](https://github.com/WebAssembly/table64/53) +- v8/chrome: [WIP](https://issues.chromium.org/issues/338024338) +- Firefox: [WIP](https://bugzilla.mozilla.org/show_bug.cgi?id=1893643) +- Safari: - +- wabt: Done +- binaryen: Done +- emscripten: Done + +## Motivation + +[WebAssembly linear memory objects][memory object] have sizes measured in +[pages][memory page]. Each page is 65536 (216) bytes. In WebAssembly +version 1, a linear memory can have at most 65536 pages, for a total of +232 bytes (4 [gibibytes][gibibyte]). + +In addition to this page limit, all [memory instructions][] currently use the +[`i32` type][i32] as a memory index. This means they can address at most +232 bytes as well. + +For many applications, 4 gibibytes of memory is enough. Using 32-bit memory +indexes is sufficient in this case, and has the additional benefit that +pointers in the producer language are smaller, which can yield memory savings. +However, for applications that need more memory than this, there are no easy +workarounds given the current WebAssembly feature set. Allowing the WebAssembly +module to choose between 32-bit and 64-bit memory indexes addresses both +concerns. + +Similarly, since WebAssembly is a Virtual [Instruction Set Architecture][ISA] +(ISA), some hosts may want to use the WebAssembly binary format as a portable +executable format, in addition to supporting other non-virtual ISAs. Nearly all +ISAs have support for 64-bit memory addresses now, and a host may not want to +have to support 32-bit memory addresses in their ABI. + +## Overview + +### Structure + +* The [limits][syntax limits] structure is changed to use `u64` + - `limits ::= {min u64, max u64?}` + +* A new `addrtype` can be either `i32` or `i64` + - `addrtype ::= i32 | i64` + +* The [memory type][syntax memtype] and [table type][syntax tabletype] + structures are extended to include an address type + - `memtype ::= addrtype limits` + - `tabletype ::= addrtype limits reftype` + +* The [memarg][syntax memarg] immediate is changed to allow a 64-bit offset + - `memarg ::= {offset u64, align u32}` + +### Validation + +* Address types are classified by their value range: + - ``` + ---------------- + ⊦ i32 : 2**16 + ``` + - ``` + ---------------- + ⊦ i64 : 2**48 + ``` + +* [Memory page limits][valid limits] and [Table entry limits][valid limits] are + classified by their respective address types + - ``` + ⊦ it : k n <= k (m <= k)? (n < m)? + ------------------------------------------- + ⊦ { min n, max m? } : it + ``` + +* Memory and Table types are validated accordingly: + - ``` + ⊦ limits : it + -------------- + ⊦ it limits ok + ``` + +* All [memory instructions][valid meminst] are changed to use the address type, + and the offset must also be in range of the address type + - t.load memarg + - ``` + C.mems[0] = at limits 2**memarg.align <= |t|/8 memarg.offset < 2**|at| + -------------------------------------------------------------------------- + C ⊦ t.load memarg : [at] → [t] + ``` + - t.loadN_sx memarg + - ``` + C.mems[0] = at limits 2**memarg.align <= N/8 memarg.offset < 2**|at| + ------------------------------------------------------------------------ + C ⊦ t.loadN_sx memarg : [at] → [t] + ``` + - t.store memarg + - ``` + C.mems[0] = at limits 2**memarg.align <= |t|/8 memarg.offset < 2**|at| + -------------------------------------------------------------------------- + C ⊦ t.store memarg : [at t] → [] + ``` + - t.storeN_sx memarg + - ``` + C.mems[0] = at limits 2**memarg.align <= N/8 memarg.offset < 2**|t| + ------------------------------------------------------------------------ + C ⊦ t.storeN_sx memarg : [at t] → [] + ``` + - memory.size + - ``` + C.mems[0] = at limits + --------------------------- + C ⊦ memory.size : [] → [at] + ``` + - memory.grow + - ``` + C.mems[0] = at limits + ----------------------------- + C ⊦ memory.grow : [at] → [at] + ``` + - memory.fill + - ``` + C.mems[0] = at limits + ----------------------------- + C ⊦ memory.fill : [at i32 at] → [] + ``` + - memory.copy + - ``` + C.mems[0] = at limits + ----------------------------- + C ⊦ memory.copy : [at at at] → [] + ``` + - memory.init x + - ``` + C.mems[0] = at limits C.datas[x] = ok + ------------------------------------------- + C ⊦ memory.init : [at i32 i32] → [] + ``` + - (and similar for memory instructions from other proposals) + +* [Table instructions][valid tableinst] are changed to use the address type + - call_indirect x y + - ``` + C.tables[x] = at limits t C.types[y] = [t1*] → [t2*] + ------------------------------------------------------- + C ⊦ call_indirect x y : [t1* at] → [t2*] + ``` + - table.get x + - ``` + C.tables[x] = at limits t + ------------------------------ + C ⊦ table.get x : [at] → [t] + ``` + - table.set x + - ``` + C.tables[x] = at limits t + ------------------------------ + C ⊦ table.set x : [at t] → [] + ``` + - table.size x + - ``` + C.tables[x] = at limits t + ------------------------------ + C ⊦ table.size x : [] → [at] + ``` + - table.grow x + - ``` + C.tables[x] = at limits t + ------------------------------- + C ⊦ table.grow x : [t at] → [at] + ``` + - table.fill x + - ``` + C.tables[x] = at limits t + ---------------------------------- + C ⊦ tables.fill x : [at t at] → [] + ``` + - table.copy x y + - ``` + C.tables[d] = aN limits t C.tables[s] = aM limits t K = min {aN, AM} + ----------------------------------------------------------------------------- + C ⊦ table.copy d s : [aN aM aK] → [] + ``` + - table.init x y + - ``` + C.tables[x] = at limits t C.elems[y] = ok + ----------------------------------------------- + C ⊦ table.init x y : [at i32 i32] → [] + ``` + +* The [SIMD proposal][simd] extends `t.load memarg` and `t.store memarg` + above such that `t` may now also be `v128`, which accesses a 16-byte quantity + in memory that is also 16-byte aligned. + + In addition to this, it also has these SIMD specific memory operations (see + [SIMD proposal][simd] for full semantics): + - `v128.loadN_zero memarg` (where N = 32/64): + Load a single 32-bit or 64-bit element into the lowest bits of a `v128` + vector, and initialize all other bits of the `v128` vector to zero. + - `v128.loadN_splat memarg` (where N = 8/16/32/64): + Load a single element and splat to all lanes of a `v128` vector. The natural + alignment is the size of the element loaded. + - `v128.loadN_lane memarg v128 immlaneidx` (where N = 8/16/32/64): + Load a single element from `memarg` into the lane of the `v128` specified in + the immediate mode operand `immlaneidx`. The values of all other lanes of + the `v128` are bypassed as is. + - `v128.storeN_lane memarg v128 immlaneidx` (where N = 8/16/32/64): + Store into `memarg` the lane of `v128` specified in the immediate mode + operand `immlaneidx`. + - `v128.loadL_sx memarg` (where L is `8x8`/`16x4`/`32x2`, and sx is `s`/`u`): + Fetch consecutive integers up to 32-bit wide and produce a vector with lanes + up to 64 bits. The natural alignment is 8 bytes. + + All these operations now take 64-bit address operands when used with a + 64-bit memory. + +* The [Threads proposal][threads] has `atomic` versions of `t.load`, `t.store`, + (and `t.loadN_u` / `t.storeN_u`, no sign-extend) specified above, except with + `.` replaced by `.atomic.`, and the guarantee of ordering of accesses being + sequentially consistent. + + In addition to this, it has the following memory operations (see + [Threads proposal][threads] for full semantics): + - `t.atomic.rmwN.op_u memarg` (where t = 32/64, N = 8/16/32 when < t or empty + otherwise, op is `add`/`sub`/`and`/`or`/`xor`/`xchg`/`cmpxchg`, and `_u` + only present when N is not empty): + The first 6 operations atomically read a value from an address, modify the + value, and store the resulting value to the same address. They then return + the value read from memory before the modify operation was performed. + In the case of `cmpxchg`, the operands are an address, an expected value, + and a replacement value. If the loaded value is equal to the expected value, + the replacement value is stored to the same memory address. If the values + are not equal, no value is stored. In either case, the loaded value is + returned. + - `memory.atomic.waitN` (where N = 32/64): + The wait operator take three operands: an address operand, an expected + value, and a relative timeout in nanoseconds as an `i64`. The return value + is `0`, `1`, or `2`, returned as an `i32`. + - `memory.atomic.notify`: + The notify operator takes two operands: an address operand and a count as an + unsigned `i32`. The operation will notify as many waiters as are waiting on + the same effective address, up to the maximum as specified by count. The + operator returns the number of waiters that were woken as an unsigned `i32`. + + All these operations now take 64-bit address operands when used with a + 64-bit memory. + +* The [Multi-memory proposal][multi memory] extends each of these instructions + with one or two memory index immediates. The address type for that memory will + be used. For example, + - memory.size x + - ``` + C.mems[x] = at limits + --------------------------- + C ⊦ memory.size x : [] → [at] + ``` + + `memory.copy` has two memory index immediates, so will have multiple possible + signatures: + - memory.copy d s + - ``` + C.mems[d] = aN limits C.mems[s] = aM limits K = min {aN, aM} + --------------------------------------------------------------- + C ⊦ memory.copy d s : [aN aM aK] → [] + ``` + +* [Data segment validation][valid data] uses the address type + - ``` + C.mems[0] = at limits C ⊦ expr: [at] C ⊦ expr const + ------------------------------------------------------- + C ⊦ {data x, offset expr, init b*} ok + ``` + + +### Execution + +* [Memory instances][exec mem] are extended to have 64-bit vectors and a `u64` + max size + - `meminst ::= { data vec64(byte), max u64? }` + +* [Memory instructions][exec meminst] use the address type instead of `i32` + - `t.load memarg` + - `t.loadN_sx memarg` + - `t.store memarg` + - `t.storeN memarg` + - `memory.size` + - `memory.grow` + - (spec text omitted) + +* [memory.grow][exec memgrow] has behavior that depends on the address type: + - for `i32`: no change + - for `i64`: check for a size greater than 264 - 1, and return + 264 - 1 when `memory.grow` fails. + +* [Memory import matching][exec memmatch] requires that the address type matches + - ``` + at_1 = at_2 ⊦ limits_1 <= limits_2 + ---------------------------------------- + ⊦ mem at_1 limits_1 <= mem at_2 limits_2 + ``` + +* Bounds checking is required to be the same as for 32-bit memories, that is, + the address + offset (a `u65`) of a load or store operation is required to be + checked against the current memory size and trap if out of range. + + It is expected that the cost of this check remains low, if an implementation + can implement the address check with a branch, and the offset separately using a + guard page for all smaller offsets. Repeated accesses over the same address and + different offsets allow simple elimination of subsequent checks. + +### Binary format + +* The [limits][binary limits] structure also encodes an additional value to + indicate the address type + - ``` + limits ::= 0x00 n:u32 ⇒ i32, {min n, max ϵ}, 0 + | 0x01 n:u32 m:u32 ⇒ i32, {min n, max m}, 0 + | 0x02 n:u32 ⇒ i32, {min n, max ϵ}, 1 ;; from threads proposal + | 0x03 n:u32 m:u32 ⇒ i32, {min n, max m}, 1 ;; from threads proposal + | 0x04 n:u64 ⇒ i64, {min n, max ϵ}, 0 + | 0x05 n:u64 m:u64 ⇒ i64, {min n, max m}, 0 + | 0x06 n:u64 ⇒ i64, {min n, max ϵ}, 1 ;; from threads proposal + | 0x07 n:u64 m:u64 ⇒ i64, {min n, max m}, 1 ;; from threads proposal + ``` + +* The [memory type][binary memtype] structure is extended to use this limits + encoding + - ``` + memtype ::= (at, lim, _):limits ⇒ at lim + ``` + +* The [memarg][binary memarg]'s offset is read as `u64` + - `memarg ::= a:u32 o:u64` + +### Text format + +* There is a new address type: + - ``` + addrtype ::= 'i32' ⇒ i32 + | 'i64' ⇒ i64 + ``` + +* The [memory type][text memtype] definition is extended to allow an optional + address type, which must be either `i32` or `i64` + - ``` + memtype ::= lim:limits ⇒ i32 lim + | at:addrtype lim:limits ⇒ at lim + ``` + +* The [memory abbreviation][text memabbrev] definition is extended to allow an + optional address type too, which must be either `i32` or `i64` + - ``` + '(' 'memory' id? address_type? '(' 'data' b_n:datastring ')' ')' === ... + ``` + + +[memory object]: https://webassembly.github.io/spec/core/syntax/modules.html#memories +[memory page]: https://webassembly.github.io/spec/core/exec/runtime.html#page-size +[gibibyte]: https://en.wikipedia.org/wiki/Gibibyte +[i32]: https://webassembly.github.io/spec/core/syntax/types.html#syntax-valtype +[memory instructions]: https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions +[ISA]: https://en.wikipedia.org/wiki/Instruction_set_architecture +[syntax limits]: https://webassembly.github.io/spec/core/syntax/types.html#syntax-limits +[syntax tabletype]: https://webassembly.github.io/spec/core/syntax/types.html#table-types +[syntax memtype]: https://webassembly.github.io/spec/core/syntax/types.html#memory-types +[syntax memarg]: https://webassembly.github.io/spec/core/syntax/instructions.html#syntax-memarg +[valid limits]: https://webassembly.github.io/spec/core/valid/types.html#limits +[valid meminst]: https://webassembly.github.io/spec/core/valid/instructions.html#memory-instructions +[valid tableinst]: https://webassembly.github.io/spec/core/valid/instructions.html#table-instructions +[valid data]: https://webassembly.github.io/spec/core/valid/modules.html#data-segments +[exec mem]: https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances +[exec meminst]: https://webassembly.github.io/spec/core/exec/instructions.html#memory-instructions +[exec memgrow]: https://webassembly.github.io/spec/core/exec/instructions.html#exec-memory-grow +[exec memmatch]: https://webassembly.github.io/spec/core/exec/modules.html#memories +[binary limits]: https://webassembly.github.io/spec/core/binary/types.html#limits +[binary memtype]: https://webassembly.github.io/spec/core/binary/types.html#memory-types +[binary memarg]: https://webassembly.github.io/spec/core/binary/instructions.html#binary-memarg +[text memtype]: https://webassembly.github.io/spec/core/text/types.html#text-memtype +[text memabbrev]: https://webassembly.github.io/spec/core/text/modules.html#text-mem-abbrev +[multi memory]: https://github.com/webassembly/multi-memory +[simd]: https://github.com/webassembly/simd +[threads]: https://github.com/webassembly/threads diff --git a/proposals/multi-value/Overview.md b/proposals/multi-value/Overview.md index 42952f0e4..c32af7710 100644 --- a/proposals/multi-value/Overview.md +++ b/proposals/multi-value/Overview.md @@ -9,7 +9,7 @@ - instructions: `value* -> value?` - blocks: `[] -> value?` -* In a stack machine, these asymmetries are artifical restrictions +* In a stack machine, these asymmetries are artificial restrictions - were imposed to simplify the initial WebAssembly release (multiple results deferred to post-MVP) - can easily be lifted by generalising to value* -> value* @@ -32,7 +32,7 @@ - loop labels can have arguments - can represent phis on backward edges - enable future `pick` operator to cross block boundary - - macro definability of instructons with inputs + - macro definability of instructions with inputs * `i32.select3` = `dup if ... else ... end` diff --git a/proposals/reference-types/Overview.md b/proposals/reference-types/Overview.md index 670658f2b..5a183b64e 100644 --- a/proposals/reference-types/Overview.md +++ b/proposals/reference-types/Overview.md @@ -95,8 +95,8 @@ New/extended instructions: * The new instruction `table.fill` fills a range in a table with a value. - `table.fill $x : [i32 t i32] -> []` - iff `$x : table t` - - the first operand is the start index of the range, the third operand its length (analoguous to `memory.fill`) - - traps when range+length > size of the table, but only after filling range up to size (analoguous to `memory.fill`) + - the first operand is the start index of the range, the third operand its length (analogous to `memory.fill`) + - traps when range+length > size of the table, but only after filling range up to size (analogous to `memory.fill`) * The `table.init` instruction takes an additional table index as immediate. - `table.init $x $y : [i32 i32 i32] -> []` diff --git a/proposals/relaxed-simd/Entropy.md b/proposals/relaxed-simd/Entropy.md new file mode 100644 index 000000000..52898e233 --- /dev/null +++ b/proposals/relaxed-simd/Entropy.md @@ -0,0 +1,57 @@ +# Entropy, Compat and Usage patterns + +The Relaxed SIMD proposal adds new operations that take advantage of the underlying hardware for accelerated performance by loosening the portability constraints of fixed-width SIMD, or adding new instructions that introduce local non-determinisim. This introduces a few potential risks specifically for browser engine implementations, though it’s possible that one or more of these might be applicable to other environments: +* Identifying underlying characteristics of the device (processor information if the engine that implements this proposal used the most optimal lowerings) +* Possibly identifying information about the browser currently in use (i.e. if a sufficiently motivated user writes a hand tuned Wasm function when engines use different lowerings for instructions) +* Compatibility risks, i.e. if code compiled for one browser works differently on a different browser (This is specific to observable behavior changes, and not performance differences) + +This document is an attempt to collate information that is already available in the issues filed for each of the operations on fingerprinting risk, quantify the risk for fingerprinting/compat issues and provide some information about usage patterns. + +## Instruction summary + +* **relaxed i8x16.swizzle**
+ *Entropy exposed:* Differences between x86/ARM
+ *Deterministic lowering:* Available
+ *Compat risk:* Low, as the differences exposed are for out of range indices
+ [*Issue link*](https://github.com/WebAssembly/relaxed-simd/issues/22) +* **relaxed i32x4.trunc_{f32x4, f64x2} operations**
+ *Entropy exposed:* Differences between x86/ARM
+ *Deterministic lowering:* Available
+ *Compat risk:* Low, as the differing behavior is for out of range values, and NaNs
+ [*Issue link*](https://github.com/WebAssembly/relaxed-simd/issues/21)
+* **qfma, qfmns**
+ *Entropy exposed:* Differences between hardware that has native FMA support, and hardware that does not.
+ *Deterministic lowering:* Not available, but depending on underlying hardware, the results can only be fused, or unfused.
+ *Compat risk:* Potentially divergent behavior based on hardware FMA support
+ *Mitigating reasons to include:* Most modern hardware do have native FMA support, performance wins are significant, operations are difficult to emulate
+ [*Issue link*](https://github.com/WebAssembly/simd/pull/79)
+* **{i8x16, i16x8, i32x4, i64x2}.laneselect**
+ *Entropy exposed:* x86/ARM
+ *Deterministic lowering:* Available
+ *Compat risk:* Medium, architectures vary on which bit is used for lane selection
+ [*Issue link*](https://github.com/WebAssembly/relaxed-simd/issues/17)
+* **{f32x4, f64x2}.{min,max}**
+ *Entropy exposed:* x86/ARM
+ *Deterministic lowering:* Available
+ *Compat risk:* Low, varying outputs when one of the inputs is NaN, or +0, -0
+ [*Issue link*](https://github.com/WebAssembly/relaxed-simd/issues/33)
+* **I16x8.relaxed_q15mulr_s**
+ *Entropy exposed:* x86/ARM
+ *Deterministic lowering:* Available
+ *Compat risk:* Low, different behaviors only in the overflow case
+ [*Issue link*](https://github.com/WebAssembly/relaxed-simd/issues/40)
+* **Dot product instructions**
+ *Entropy exposed:* x86/ARM, and whether the Dot product extension is supported in the most optimal codegen
+ *Deterministic lowering:* Available, but only when not using the most optimal codegen
+ *Compat risk:* Medium for architectures that support the Dot product extensions as they vary in saturating/wrapping behavior of intermediate results
*Mitigating reasons to include:* [Performance](https://docs.google.com/presentation/d/1xlyO1ly2Fbo2Up5ZuV_BTSwiNpCwPygag09XQRjclSA/edit#slide=id.g1fee95a4c4f_0_0)
+ [*Issue link*](https://github.com/WebAssembly/relaxed-simd/issues/52) + +## Usage patterns + +One of the things to note here, for compat especially, is to lay out the usage patterns, or specifically when these instructions are generated. As the proposal is still in the experimental state, the only way to currently experiment with these instructions are to call the clang built-ins directly, and experiment with them on an engine that has these instructions available as a prototype. + +In the future, these can be invoked using: + * Wasm Intrinsic header files + * Possibly in the autovectorizer, but only when flags like `-ffast-math` are used, that assume inputs and/or results are not NaNs or +-Infs. + +The thing to note here is that by either explicitly calling the intrinsics, or using specific compiler flags is a high enough threshold that this type of local non-determinism is not something a user would encounter by default, i.e. these instructions can only be used deliberately, and the proposal assumes a specialized usage. diff --git a/proposals/relaxed-simd/Overview.md b/proposals/relaxed-simd/Overview.md new file mode 100644 index 000000000..8fe79bfec --- /dev/null +++ b/proposals/relaxed-simd/Overview.md @@ -0,0 +1,357 @@ +# Relaxed SIMD proposal + +## Summary + +This proposal adds a set of useful SIMD instructions that introduce local +non-determinism (where the results of the instructions may vary based on +hardware support). + +## Motivation + +Applications running on Wasm SIMD cannot take full advantage of hardware +capabilities. There are 3 reasons: + +1. Instruction depends on hardware support +2. Approximate instructions that are underspecified in hardware +3. Some SIMD instructions penalize particular architecture + +See [these +slides](https://docs.google.com/presentation/d/1Qnx0nbNTRYhMONLuKyygEduCXNOv3xtWODfXfYokx1Y/edit?usp=sharing) +for more details. + +## Overview + +Broadly, there are three categories of instructions that fit into the Relaxed SIMD proposal: + +1. Integer instructions where the inputs are interpreted differently (e.g. + swizzle, 4-D dot-product) +2. Floating-point instructions whose behavior for out-of-range and NaNs differ + (e.g. float-to-int conversions, float min/max) +3. Floating-point instructions where the precision or order of operations + differ (e.g. FMA, reciprocal instructions, sum reduction) + +Example of some instructions we would like to add: + +- Fused Multiply Add (single rounding if hardware supports it, double rounding if not) +- Approximate reciprocal/reciprocal sqrt +- Relaxed Swizzle (implementation defined out of bounds behavior) +- Relaxed Rounding Q-format Multiplication (optional saturation) + +### Consistency + +This proposal introduces non-deterministic instructions - given the same +inputs, two calls to the same instruction can return different results. For +example: + +```wast +(module + (func (param v128 v128 v128) + (f32x4.qfma (local.get 0) (local.get 1) (local.get 2)) ;; (1) + ;; some other computation + (f32x4.qfma (local.get 0) (local.get 1) (local.get 2)))) ;; (2) +``` + +The same instruction at `(1)` and `(2)`, when given the same inputs, can return +two different results. This is compliant as the instruction is +non-deterministic, though unlikely on certain embeddings like the Web (where +the same implementation for `f32x4.qfma` is likely to be used for all calls to +that instruction). One can imagine splitting an application's module and +running them on multiple runtimes, where the runtimes produce +different results - this can be surprising to the application. + +The specification is updated with the idea of "Relaxed operations": + +> Some operators are host-dependent, because the set of possible results may +> depend on properties of the host environment (such as hardware). Technically, +> each such operator produces a fixed-size list of sets of allowed values. For +> each execution of the operator in the same environment, only values from the +> set at the same position in the list are returned, i.e., each environment +> globally chooses a fixed projection for each operator. + +## Instructions + +`IMPLEMENTATION_DEFINED_ONE_OF(...)` returns any one of the results in the argument list, depending on the implementation. + +### Relaxed swizzle + +- `relaxed i8x16.swizzle(a : v128, s : v128) -> v128` + +`relaxed i8x16.swizzle(a, s)` selects lanes from `a` using indices in `s`, +indices in the range `[0,15]` will select the `i`-th element of `a`, the result +for any out of range indices is implementation-defined (i.e. if the index is +`[16-255]`. + +```python +def relaxed_i8x16_swizzle(a : i8x16, s : i8x16): + result = [] + for i in range(16): + if s[i] < 16: + result[i] = a[s[i]] + else if s[i] < 128: + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(0, a[s[i]%16]) + else: + result[i] = 0 + return result +``` + +### Float/Double to int conversions + +- `relaxed i32x4.trunc_f32x4_s` (relaxed version of `i32x4.trunc_sat_f32x4_s`) +- `relaxed i32x4.trunc_f32x4_u` (relaxed version of `i32x4.trunc_sat_f32x4_u`) +- `relaxed i32x4.trunc_f64x2_s_zero` (relaxed version of `i32x4.trunc_sat_f64x2_s_zero`) +- `relaxed i32x4.trunc_f64x2_u_zero` (relaxed version of `i32x4.trunc_sat_f64x2_u_zero`) + +These instructions have the same behavior as the non-relaxed instructions for +lanes that are in the range of an `i32` (signed or unsigned depending on the +instruction). The result of lanes which contain NaN is implementation defined, +either 0 or `INT32_MAX` for signed and `UINT32_MAX` for unsigned. The result of +lanes which are out of bounds of `INT32` or `UINT32` is implementation defined, +it can be either the saturated result or `INT32_MAX` for signed and `UINT32_MAX` +for unsigned. + +```python +def relaxed_i32x4_trunc_f32x4_s(a : f32x4) -> i32x4: + result = [0, 0, 0, 0] + for i in range(4): + if isnan(a[i]): + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(0, INT32_MIN) + continue + r = truncate(a[i]) + if r < INT32_MIN: + result[i] = INT32_MIN + elif r > INT32_MAX + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(INT32_MIN, INT32_MAX) + else: + result[i] = r + +def relaxed_i32x4_trunc_f32x4_u(a : f32x4) -> i32x4: + result = [0, 0, 0, 0] + for i in range(4): + if isnan(a[i]): + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(0, UINT32_MAX) + continue + r = truncate(a[i]) + if r < UINT32_MIN: + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(UINT32_MIN, UINT32_MAX) + elif r > UINT32_MAX: + result[i] = UINT32_MAX + else: + result[i] = r + +def relaxed_i32x4_trunc_f64x2_zero_s(a : f64x2) -> i32x4: + result = [0, 0, 0, 0] + for i in range(2): + if isnan(a[i]): + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(0, INT32_MIN) + continue + r = truncate(a[i]) + if r < INT32_MIN: + result[i] = INT32_MIN + elif r > INT32_MAX + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(INT32_MIN, INT32_MAX) + else: + result[i] = r + +def relaxed_i32x4_trunc_f64x2_zero_u(a : f64x2) -> i32x4: + result = [0, 0, 0, 0] + for i in range(2): + if isnan(a[i]): + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(0, UINT32_MAX) + continue + r = truncate(a[i]) + if r < UINT32_MIN: + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(UINT32_MIN, UINT32_MAX) + elif r > UINT32_MAX: + result[i] = UINT32_MAX + else: + result[i] = r +``` + +### Relaxed fused multiply-add and fused negative multiply-add + +- `relaxed f32x4.madd` +- `relaxed f32x4.nmadd` +- `relaxed f64x2.madd` +- `relaxed f64x2.nmadd` + +All the instructions take 3 operands, `a`, `b`, `c`, perform `a * b + c` or `-(a * b) + c`: + +- `relaxed f32x4.madd(a, b, c) = a * b + c` +- `relaxed f32x4.nmadd(a, b, c) = -(a * b) + c` +- `relaxed f64x2.madd(a, b, c) = a * b + c` +- `relaxed f64x2.nmadd(a, b, c) = -(a * b) + c` + +where: + +- the intermediate `a * b` is be rounded first, and the final result rounded again (for a total of 2 roundings), or +- the entire expression evaluated with higher precision and then only rounded once (if supported by hardware). + +### Relaxed laneselect + +- `i8x16.laneselect(a: v128, b: v128, m: v128) -> v128` +- `i16x8.laneselect(a: v128, b: v128, m: v128) -> v128` +- `i32x4.laneselect(a: v128, b: v128, m: v128) -> v128` +- `i64x2.laneselect(a: v128, b: v128, m: v128) -> v128` + +Select lanes from `a` or `b` based on masks in `m`. If each lane-sized mask in `m` has all bits set or all bits unset, these instructions behave the same as `v128.bitselect`. Otherwise, the result is implementation defined. + +```python +def laneselect(a : v128, b : v128, m: v128, lanes : int): + result = [0] * lanes + for i in range(lanes): + mask = m[i] + if mask == ~0: + result[i] = a[i] + elif mask == 0: + result[i] = b[i] + else topbit(mask) == 1: + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(bitselect(a[i], b[i], mask), a[i]) + else: + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(bitselect(a[i], b[i], mask), b[i]) + return result +``` + +### Relaxed min and max + +- `f32x4.min(a: v128, b: v128) -> v128` +- `f32x4.max(a: v128, b: v128) -> v128` +- `f64x2.min(a: v128, b: v128) -> v128` +- `f64x2.max(a: v128, b: v128) -> v128` + +Return the lane-wise minimum or maximum of two values. If either values is NaN, or the values are -0.0 and +0.0, the return value is implementation-defined. + +```python +def min_or_max(a : v128, b : v128, lanes : int, is_min : bool): + result = [] + for i in range(lanes): + if isNaN(a[i]) or isNaN(b[i]): + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(a[i], b[i]) + elif (a[i] == -0.0 && b[i] == +0.0) or (a[i] == +0.0 && b[i] == -0.0): + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(a[i], b[i]) + else: + result[i] = is_min ? min(a, b) : max(a, b) + return result +``` + +### Relaxed Rounding Q-format Multiplication + +- `i16x8.q15mulr_s(a: v128, b: v128) -> v128` + +Returns the multiplication of 2 fixed-point numbers in Q15 format. If both +inputs are `INT16_MIN`, the result overflows, and the return value is +implementation defined (either `INT16_MIN` or `INT16_MAX`). + +```python +def q15mulr(a, b): + result = [] + for i in range(lanes): + if (a[i] == INT16_MIN && b[i] == INT16_MIN): + result[i] = IMPLEMENTATION_DEFINED_ONE_OF(INT16_MIN, INT16_MAX) + else: + result[i] = (a[i] * b[i] + 0x4000) >> 15 + return result +``` + +### Relaxed integer dot product + +- `i16x8.dot_i8x16_i7x16_s(a: v128, b: v128) -> v128` +- `i32x4.dot_i8x16_i7x16_add_s(a: v128, b:v128, c:v128) -> v128` + +Returns the multiplication of 8-bit elements (signed or unsigned) by 7-bit +elements (unsigned) with accumulation of adjacent products. The `i32x4` versions +allows for accumulation into another vector. + +When the second operand of the product has the high bit set in a lane, that +lane's result is implementation defined. + +```python +def i16x8_dot_i8x16_i7x16_s(a, b): + intermediate = [] + result = [] + for i in range(16): + if (b[i] & 0x80): + lhs = as_signed(a[i]) + rhs = IMPLEMENTATION_DEFINED_ONE_OF(as_signed(b[i]), as_unsigned(b[i])) + intermediate[i] = lhs * rhs + else: + intermediate[i] = as_signed(a[i]) * b[i] + for i in range(0, 16, 2): + result[i/2] = IMPLEMENTATION_DEFINED_ONE_OF( + intermediate[i] + intermediate[i+1], + saturate(intermediate[i] + intermediate[i+1])) + +def i32x4.dot_i8x16_i7x16_add_s(a, b, c): + intermediate = [] + tmp = [] + result = [] + for i in range(16): + if (b[i] & 0x80): + lhs = as_signed(a[i]) + rhs = IMPLEMENTATION_DEFINED_ONE_OF(as_signed(b[i]), as_unsigned(b[i])) + intermediate[i] = lhs * rhs + else: + intermediate[i] = as_signed(a[i]) * b[i] + + for i in range(0, 16, 2): + tmp[i/2] = IMPLEMENTATION_DEFINED_ONE_OF( + intermediate[i] + intermediate[i+1], + saturate(intermediate[i] + intermediate[i+1])) + + for i in range(0, 8, 2): + dst[i/4] = tmp[i] + tmp[i+1] + + for i in range(0, 4): + dst[i] += c[i] + +saturate(x) = min(INT16_MAX, max(INT16_MIN, x)) +``` + +## Binary format + +All opcodes have the `0xfd` prefix (same as SIMD proposal), which are omitted in the table below. +Opcodes `0x100` to `0x12F` (32 opcodes) are reserved for this proposal. + +Note: "prototype opcode" refers to the opcodes that were used in prototyping, which +where chosen to fit into the holes in the opcode space of SIMD proposal. Going +forward, the opcodes for relaxed-simd specification will be the ones in the +"opcode" column, and it will take some time for tools and engines to update. + +| instruction | opcode | prototype opcode | +| ------------------------------------- | -------------- | ---------------- | +| `i8x16.relaxed_swizzle` | 0x100 | 0xa2 | +| `i32x4.relaxed_trunc_f32x4_s` | 0x101 | 0xa5 | +| `i32x4.relaxed_trunc_f32x4_u` | 0x102 | 0xa6 | +| `i32x4.relaxed_trunc_f64x2_s_zero` | 0x103 | 0xc5 | +| `i32x4.relaxed_trunc_f64x2_u_zero` | 0x104 | 0xc6 | +| `f32x4.relaxed_madd` | 0x105 | 0xaf | +| `f32x4.relaxed_nmadd` | 0x106 | 0xb0 | +| `f64x2.relaxed_madd` | 0x107 | 0xcf | +| `f64x2.relaxed_nmadd` | 0x108 | 0xd0 | +| `i8x16.relaxed_laneselect` | 0x109 | 0xb2 | +| `i16x8.relaxed_laneselect` | 0x10a | 0xb3 | +| `i32x4.relaxed_laneselect` | 0x10b | 0xd2 | +| `i64x2.relaxed_laneselect` | 0x10c | 0xd3 | +| `f32x4.relaxed_min` | 0x10d | 0xb4 | +| `f32x4.relaxed_max` | 0x10e | 0xe2 | +| `f64x2.relaxed_min` | 0x10f | 0xd4 | +| `f64x2.relaxed_max` | 0x110 | 0xee | +| `i16x8.relaxed_q15mulr_s` | 0x111 | 0x111 | +| `i16x8.relaxed_dot_i8x16_i7x16_s` | 0x112 | 0x112 | +| `i32x4.relaxed_dot_i8x16_i7x16_add_s` | 0x113 | 0x113 | +| Reserved | 0x114 - 0x12F | | + +## References + +- Poll for phase 1 + [presentation](https://docs.google.com/presentation/d/1Qnx0nbNTRYhMONLuKyygEduCXNOv3xtWODfXfYokx1Y/edit?usp=sharing) + and [meeting + notes](https://github.com/WebAssembly/meetings/blob/master/main/2021/CG-03-16.md) +- Poll for phase 2 + [slides](https://docs.google.com/presentation/d/1zyRqfgGU7HdoVw9QiKaVYifozbytPhNLHUW9jQjPzLk/edit?usp=sharing) + and [meeting + notes](https://github.com/WebAssembly/meetings/blob/main/main/2021/CG-11-09.md#update-on-relaxed-simd-fpenv-discussions-and-poll-for-phase-2-zhi-an-ng-15-min) +- Poll for phase 3 + [slides](https://docs.google.com/presentation/d/1ofBkgbW2AjYM4oayjTPTH3PCbyEn6W-q3GN67qSEigQ/edit?usp=sharing) + and [meeting + notes](https://github.com/WebAssembly/meetings/blob/main/main/2022/CG-04-12.md) +- [SIMD proposal](https://github.com/WebAssembly/simd) diff --git a/proposals/simd/SIMD.md b/proposals/simd/SIMD.md index 9f2c802a4..8e1d2aaaf 100644 --- a/proposals/simd/SIMD.md +++ b/proposals/simd/SIMD.md @@ -18,7 +18,7 @@ packed data in one instruction. These are commonly used to improve performance for multimedia applications. The set of SIMD instructions in hardware is large, and varies across different versions of hardware. This proposal is comprised of a portable subset of operations that in most cases map to commonly used -instructions in mordern hardware. +instructions in modern hardware. # Types @@ -136,14 +136,14 @@ Accessing WebAssembly module imports or exports containing SIMD Type from JavaSc ### Module Function Imports -Calling an imported function from JavaScript when the function arguments or result is of type v128 will cause the host function to immidiately throw a [`TypeError`](https://tc39.github.io/ecma262/#sec-native-error-types-used-in-this-standard-typeerror). +Calling an imported function from JavaScript when the function arguments or result is of type v128 will cause the host function to immediately throw a [`TypeError`](https://tc39.github.io/ecma262/#sec-native-error-types-used-in-this-standard-typeerror). ### Exported Function Exotic Objects -Invoking the [[Call]] method of an Exported Function Exotic Object when the function type of its [[Closure]] has an argument or result of type v128 will cause the host function to immidiately throw a [`TypeError`](https://tc39.github.io/ecma262/#sec-native-error-types-used-in-this-standard-typeerror). +Invoking the [[Call]] method of an Exported Function Exotic Object when the function type of its [[Closure]] has an argument or result of type v128 will cause the host function to immediately throw a [`TypeError`](https://tc39.github.io/ecma262/#sec-native-error-types-used-in-this-standard-typeerror). -## WebAssembly Module Instatiation +## WebAssembly Module Instantiation Instantiating a WebAssembly Module from a Module moduleObject will throw a LinkError exception, when the global's valtype is v128 and the imported objects type is not WebAssembly.Global. diff --git a/proposals/stack-switching/Explainer.md b/proposals/stack-switching/Explainer.md index 5915ad2b5..f370591c2 100644 --- a/proposals/stack-switching/Explainer.md +++ b/proposals/stack-switching/Explainer.md @@ -21,6 +21,9 @@ validation rules to facilitate stack-switching. 1. [Suspending continuations](#suspending-continuations) 1. [Partial continuation application](#partial-continuation-application) 1. [Continuation lifetime](#continuation-lifetime) +1. [Further examples](#further-examples) + 1. [Extending the generator](#extending-the-generator) + 1. [Canceling tasks](#canceling-tasks) 1. [Design considerations](#design-considerations) 1. [Asymmetric switching](#asymmetric-switching) 1. [Symmetric switching](#symmetric-switching) @@ -78,12 +81,13 @@ design provides a form of *symmetric switching*. We illustrate the proposed stack-switching mechanism using two examples: generators and task scheduling. The generators example uses -asymmetric stack-switching and the task scheduling example uses -symmetric stack-switching. +asymmetric stack-switching. The task scheduling example has two +variants: the first variant uses asymmetric stack-switching and the +second variant uses symmetric stack-switching. ### Generators -The first example illustrates a generator-consumer pattern. Execution +Our first example illustrates a generator-consumer pattern. Execution switches back and forth between a generator and a consumer execution stack. Whenever execution switches from the generator to the consumer the generator also passes a value to the consumer. @@ -116,20 +120,19 @@ The overall module implementing our example has the following shape. ```wat (module $generator (type $ft (func)) - ;; Types of continuations used by the generator: + ;; Type of continuations used by the generator: ;; No need for param or result types: No data passed back to the ;; generator when resuming it, and $generator function has no return ;; values. (type $ct (cont $ft)) + (func $print (import "spectest" "print_i32") (param i32)) + ;; Tag used to coordinate between generator and consumer: The i32 param - ;; corresponds to the generated values passed; no values passed back from - ;; generator to consumer. + ;; corresponds to the generated values passed to consumer; no values passed + ;; back from generator to consumer. (tag $gen (param i32)) - - (func $print (import "spectest" "print_i32") (param i32)) - ;; Simple generator yielding values from 100 down to 1 (func $generator ...) (elem declare func $generator) @@ -147,16 +150,16 @@ manipulate suspended continuations of type `(ref $ct)`. The generator is defined as follows. ```wat -;; Simple generator yielding values from 100 down to 1 +;; Simple generator yielding values from 100 down to 1. (func $generator (local $i i32) (local.set $i (i32.const 100)) - (loop $l - ;; Suspend execution, pass current value of $i to consumer + (loop $loop + ;; Suspend execution, pass current value of $i to consumer. (suspend $gen (local.get $i)) - ;; Decrement $i and exit loop once $i reaches 0 + ;; Decrement $i and exit loop once $i reaches 0. (local.tee $i (i32.sub (local.get $i) (i32.const 1))) - (br_if $l) + (br_if $loop) ) ) ``` @@ -178,9 +181,9 @@ The consumer is defined as follows. (loop $loop (block $on_gen (result i32 (ref $ct)) - ;; Resume continuation $c + ;; Resume continuation $c. (resume $ct (on $gen $on_gen) (local.get $c)) - ;; $generator returned: no more data + ;; $generator returned: no more data. (return) ) ;; Generator suspended, stack now contains [i32 (ref $ct)] @@ -257,7 +260,7 @@ The full definition of this module can be found ### Task scheduling -The second example demonstrates how to implement task scheduling with +Our second example demonstrates how to implement task scheduling with the stack-switching instructions. Specifically, suppose we want to schedule a number of tasks, represented by functions `$task_0` to `$task_n`, to be executed concurrently. Scheduling is cooperative, @@ -276,12 +279,10 @@ This approach is illustrated by the following skeleton code. ```wat (module $scheduler1 - (type $ft (func)) ;; Continuation type of all tasks (type $ct (cont $ft)) - ;; Tag used to yield execution in one task and resume another one. (tag $yield) @@ -442,16 +443,16 @@ continuation references. The task that we switched to is now responsible for enqueuing the previous continuation (i.e., the one received as a payload) in the task list. -As a minor complication, we need to encode the fact that the -continuation switched to receives the current one as an argument in -the type of the continuations handled by all scheduling logic. This -means the type `$ct` must be recursive: a continuation of this type +As a minor complication, we must encode the fact that the continuation +switched to receives the current continuation as an argument in the +type of the continuations handled by all scheduling logic. This means +that the type `$ct` must be recursive: a continuation of this type takes a value of type `(ref null $ct)` as a parameter. In order to give the same type to continuations that have yielded execution (those created by `switch`) and those continuations that correspond to beginning the execution of a `$task_i` function (those created by `cont.new`), we add a `(ref null $ct)` parameter to all of the -`$task_i` functions. Finally, observe that the event loop passes a +`$task_i` functions. Finally, observe that the event loop passes a null continuation to any continuation it resumes, indicating to the resumed continuation that there is no previous continuation to enqueue in the task list. @@ -482,8 +483,8 @@ a task function would ordinarily return, it should instead switch to the next task. When doing so, it should pass a new flag to the target continuation to indicate that the source continuation should not be enqueued in the task list, but should instead be cancelled. Cancellation -can be implemented using another instruction, `resume_throw`, which is -described later in the document. +can be implemented using two more instructions, `resume_throw` and +`resume_throw_ref`, which are described later in the document. Full versions of `$scheduler1` and `$scheduler2` can be found [here](examples/scheduler1.wast) and [here](examples/scheduler2.wast). @@ -542,7 +543,7 @@ The following instruction creates a *suspended continuation* from a function. ```wast - cont.new $ct : [(ref $ft)] -> [(ref $ct)] + cont.new $ct : [(ref null $ft)] -> [(ref $ct)] where: - $ft = func [t1*] -> [t2*] - $ct = cont $ft @@ -560,7 +561,7 @@ continuation under a *handler*. The handler specifies what to do when control is subsequently suspended again. ```wast - resume $ct hdl* : [t1* (ref $ct)] -> [t2*] + resume $ct hdl* : [t1* (ref null $ct)] -> [t2*] where: - $ct = cont [t1*] -> [t2*] ``` @@ -580,7 +581,7 @@ The second way to invoke a continuation is to raise an exception at the control tag invocation site which causes the stack to be unwound. ```wast - resume_throw $ct $exn hdl* : [te* (ref $ct)])] -> [t2*] + resume_throw $ct $exn hdl* : [te* (ref null $ct)] -> [t2*] where: - $ct = cont [t1*] -> [t2*] - $exn : [te*] -> [] @@ -596,14 +597,27 @@ exception is being raised (the continuation is not actually being supplied a value) the parameter types for the continuation `t1*` are unconstrained. +Exceptions can also be raised at a suspension site using +`resume_throw_ref`. + +```wast + resume_throw_ref $ct hdl* : [exnref (ref null $ct)] -> [t2*] + where: + - $ct = cont [t1*] -> [t2*] +``` + +The `resume_throw_ref` instruction is identical to the `resume_throw` +instruction except that the exception it raises is given by an exnref +operand rather than by an exception tag immediate. + The third way to invoke a continuation is to perform a symmetric switch. ```wast - switch $ct1 $e : [t1* (ref $ct1)] -> [t2*] + switch $ct1 $e : [t1* (ref null $ct1)] -> [t2*] where: - $e : [] -> [t*] - - $ct1 = cont [t1* (ref $ct2)] -> [t*] + - $ct1 = cont [t1* (ref null? $ct2)] -> [t*] - $ct2 = cont [t2*] -> [t*] ``` @@ -613,8 +627,9 @@ continuation argument (`$ct1`) and a control tag performs a direct switch to the suspended peer continuation (of type `$ct1`), passing in the required parameters (including the just suspended current continuation, in order to allow the peer to switch -back again). As with `resume` and `resume_throw`, the `switch` -instruction fully consumes its suspended continuation argument. +back again). As with `resume`, `resume_throw`, and `resume_throw_ref`, +the `switch` instruction fully consumes its suspended continuation +argument. ### Suspending continuations @@ -640,7 +655,7 @@ A suspended continuation can be partially applied to a prefix of its arguments yielding another suspended continuation. ```wast - cont.bind $ct1 $ct2 : [t1* (ref $ct1)] -> [(ref $ct2)] + cont.bind $ct1 $ct2 : [t1* (ref null $ct1)] -> [(ref $ct2)] where: - $ct1 = cont [t1* t3*] -> [t2*] - $ct2 = cont [t3*] -> [t2*] @@ -671,16 +686,192 @@ preallocating one slot for each continuation argument. #### Consuming continuations -There are four different ways in which suspended continuations are -consumed (`resume,resume_throw,switch,cont.bind`). A suspended -continuation may be resumed with a particular handler with `resume`; -aborted with `resume_throw`; directly switched to via `switch`; or -partially applied with `cont.bind`. +There are five different ways in which suspended continuations are +consumed (`resume`, `resume_throw`, `resume_throw_ref`, `switch`, +`cont.bind`). A suspended continuation may be resumed with a particular +handler with `resume`; aborted with `resume_throw` or +`resume_throw_ref`; directly switched to via `switch`; or partially +applied with `cont.bind`. In order to ensure that continuations are one-shot, `resume`, -`resume_throw`, `switch`, and `cont.bind` destructively modify the -suspended continuation such that any subsequent use of the same -suspended continuation will result in a trap. +`resume_throw`, `resume_throw_ref`, `switch`, and `cont.bind` +destructively modify the suspended continuation such that any +subsequent use of the same suspended continuation will result in a +trap. + +## Further examples + +We now illustrate the use of tags with result values and the +instructions `cont.bind` and `resume_throw`, by adapting and extending +the examples of [Section +3](#introduction-to-continuation-based-stack-switching). + +### Extending the generator + +The `$generator` function in [Section 3](#generators) +produces the values 100 down to 1. It uses the tag `$gen`, defined as +`(tag $gen (param i32))`, to send values to the `$producer` function. + +We now adapt the producer to indicate to the generator when to reset +(i.e., start counting down from 100 again). The producer is adapted to +pass a boolean flag to the generator when resuming a continuation. +Correspondingly, the `$gen` tag is adapted to include an `i32` result +type for the flag: + +```wat +(tag $gen (param i32) (result i32)) +``` + +In the generator, the instruction `(suspend $gen)` now has type `[i32] +-> [i32]`: the parameter type represents the generated value (as in +the original version of the example) and the result type represents +the flag obtained back from the producer. We adapt the generator to +behave as follows, choosing between resetting or decrementing `$i`: + +```wat + (func $generator + (local $i i32) + (local.set $i (i32.const 100)) + (loop $loop + ;; Suspend execution, pass current value of $i to consumer + (suspend $gen (local.get $i)) + ;; We now have the flag on the stack given to us by the consumer, telling + ;; us whether to reset the generator or not. + (if (result i32) + (then (i32.const 100)) + (else (i32.sub (local.get $i) (i32.const 1))) + ) + (local.tee $i) + (br_if $loop) + ) + ) +``` + +In the producer, we add logic to select the value of the flag, and +pass it to the generator continuation on `resume`. However, this poses +a challenge: the continuation created with `(cont.new $ct0 (ref.func +$generator)))` has the same type as before: a continuation type with +no parameter or return types. In contrast, the type of the +continuation received in a handler block for tag `$gen` expects an +`i32` due to the result type we added to `$gen`. This means that the +producer must now manipulate two different continuation types: + +```wat +(type $ft0 (func)) +(type $ft1 (func (param i32))) +;; Types of continuations used by the generator: +;; No param or result types for $ct0: $generator function has no +;; parameters or return values. +(type $ct0 (cont $ft0)) +;; One param of type i32 for $ct1: An i32 is passed back to the +;; generator when resuming it, and $generator function has no return +;; values. +(type $ct1 (cont $ft1)) +``` + +In order to avoid making the producer function unnecessarily +complicated, we ensure that there is a single local variable that +contains the next continuation to resume; its type will be `(ref +$ct0)`. We can then use `cont.bind` to turn the continuations received +in the handler block from type `(ref $ct1)` into `(ref $ct0)` by +binding the value of the flag to be passed. + +The overall function is then defined as follows: + +```wat +(func $consumer (export "consumer") + ;; The continuation of the generator. + (local $c0 (ref $ct0)) + ;; For temporarily storing the continuation received in handler. + (local $c1 (ref $ct1)) + (local $i i32) + ;; Create continuation executing function $generator. + ;; Execution only starts when resumed for the first time. + (local.set $c0 (cont.new $ct0 (ref.func $generator))) + ;; Just counts how many values we have received so far. + (local.set $i (i32.const 1)) + + (loop $loop + (block $on_gen (result i32 (ref $ct1)) + ;; Resume continuation $c0 + (resume $ct0 (on $gen $on_gen) (local.get $c0)) + ;; $generator returned: no more data + (return) + ) + ;; Generator suspended, stack now contains [i32 (ref $ct0)] + ;; Save continuation to resume it in next iteration + (local.set $c1) + ;; Stack now contains the i32 value yielded by $generator + (call $print) + + ;; Calculate flag to be passed back to generator: + ;; Reset after the 42nd iteration + (i32.eq (local.get $i) (i32.const 42)) + ;; Create continuation of type (ref $ct0) by binding flag value. + (cont.bind $ct1 $ct0 (local.get $c1)) + (local.set $c0) + + (local.tee $i (i32.add (local.get $i) (i32.const 1))) + (br_if $loop) + ) +) +``` + +Here, we set the flag for resetting the generator exactly once (after +it has returned 42 values). + +The full version of the extended generator example can be found +[here](examples/generator-extended.wast). + +### Canceling tasks + +The task scheduling examples from [Section 3](#task-scheduling) yield +either by suspending to the scheduler running in the parent +(asymmetric variant) or by calling a scheduling function that uses +`switch` (symmetric variant). + +Suppose we wish to adapt our schedulers to impose a limit on the +number of tasks that can exist at the same time. A simple way to +enforce the limit is to cancel the task at the head of the queue +whenever an attempt is made to add a continuation to a full queue. We +can implement this behaviour with a small modification to our previous +schedulers. Instead of adding tasks to be scheduled directly to a +queue, we call the following function. + +```wat +(func $schedule_task (param $c (ref null $ct)) + ;; If the task queue is too long, cancel a task in the queue + (if (i32.ge_s (call $task_queue-count) (global.get $concurrent_task_limit)) + (then + (block $exc_handler + (try_table (catch $abort $exc_handler) + (resume_throw $ct $abort (call $task_dequeue)) + ) + ) + ) + ) + (call $task_enqueue (local.get $c)) +) +``` + +The `$schedule_task` function checks if the current number of elements +in the queue has already reached the limit. If so, the function takes +an existing continuation from the queue and calls `resume_throw` on +it. + +The `resume_throw` instruction is annotated with a newly defined tag, +`$abort`. This tag denotes an exception that will be raised at the +suspension point of the continuation. We then wrap the `resume_throw` +instruction in a `try_table`, which installs an exception handler for +`$abort`. This exception handler simply swallows the exception, which +means that the exception raised at the suspension point cannot escape +the `$schedule_task` function. The old continuation is deallocated and +the function proceeds to enqueue the new continuation. + +The full version of the symmetric scheduling example using the +`$schedule_task` function can be found +[here](examples/scheduler2-throw.wast). The changes to the asymmetric +scheduling example are analogous. ## Design considerations @@ -725,8 +916,8 @@ critical use-cases requires multi-shot continuations. ## Specification changes -This proposal is based on the [function references proposal](https://github.com/WebAssembly/function-references) -and [exception handling proposal](https://github.com/WebAssembly/exception-handling). +This proposal is based on Wasm 3.0, specifically [function references](https://github.com/WebAssembly/function-references) +and [exception handling](https://github.com/WebAssembly/exception-handling). ### Types @@ -748,6 +939,15 @@ We change the wellformedness condition for tag types to be more liberal, i.e. In other words, the return type of tag types is allowed to be non-empty. +We also introduce tag uses, which can be either tag indices or tag addresses: + +- `taguse ::= tagidx | tagaddr` + - `$e : [t1*] -> [t2*]` + - iff `C.tags[$e] = tag $ft` + - and `C.types[$ft] ~~ func [t1*] -> [t2*]` + - `ea : [t1*] -> [t2*]` + - iff `S.tags[ea].type ~~ func [t1*] -> [t2*]` + ### Instructions The new instructions and their validation rules are as follows. To simplify the presentation, we write this: @@ -793,36 +993,48 @@ This abbreviation will be formalised with an auxiliary function or other means i - and `C.types[$ft] ~~ func [te*] -> []` - and `(hdl : t2*)*` -- `hdl = (on ) | (on switch)` +- `resume_throw_ref hdl*` + - Execute a given continuation, but force it to immediately throw the given exception. + - Used to abort a continuation. + - `resume_throw_ref $ct hdl* : [exnref (ref null $ct)] -> [t2*]` + - iff `C.types[$ct] ~~ cont [t1*] -> [t2*]` + - and `(hdl : t2*)*` + +- `hdl = (on ) | (on switch)` - Handlers attached to `resume` and `resume_throw`, handling control tags for `suspend` and `switch`, respectively. - - `(on $e $l) : t*` - - iff `C.tags[$e] = tag $ft` - - and `C.types[$ft] ~~ func [t1*] -> [t2*]` + - `(on tu $l) : t*` + - iff `tu : [t1*] -> [t2*]` - and `C.labels[$l] = [t1'* (ref null? $ct)]` - and `t1* <: t1'*` - and `C.types[$ct] ~~ cont [t2'*] -> [t'*]` - and `[t2*] -> [t*] <: [t2'*] -> [t'*]` - - `(on $e switch) : t*` - - iff `C.tags[$e] = tag $ft` - - and `C.types[$ft] ~~ func [] -> [t*]` + - `(on tu switch) : t*` + - iff `tu : [] -> [t*]` -- `suspend ` +- `suspend ` - Use a control tag to suspend the current computation. - - `suspend $t : [t1*] -> [t2*]` - - iff `C.tags[$t] = tag $ft` - - and `C.types[$ft] ~~ func [t1*] -> [t2*]` + - `suspend tu : [t1*] -> [t2*]` + - iff `tu : func [t1*] -> [t2*]` -- `switch ` +- `switch ` - Switch to executing a given continuation directly, suspending the current execution. - The suspension and switch are performed from the perspective of a parent `(on $e switch)` handler, determined by the annotated control tag. - - `switch $ct1 $e : [t1* (ref null $ct1)] -> [t2*]` - - iff `C.tags[$e] = tag $ft` - - and `C.types[$ft] ~~ func [] -> [t*]` + - `switch $ct1 tu : [t1* (ref null $ct1)] -> [t2*]` + - iff `tu : [] -> [t*]` - and `C.types[$ct1] ~~ cont [t1* (ref null? $ct2)] -> [te1*]` - and `te1* <: t*` - and `C.types[$ct2] ~~ cont [t2*] -> [te2*]` - and `t* <: te2*` +In addition to these new rules, the existing rules for cast instructions `ref.test`, `ref.cast`, `br_on_cast`, and `br_on_cast_fail` are amended with an additional side condition to rule out casting of continuation types: + + - iff `rt castable` + +where `rt` is the respective target type of the cast instruction, and the `castable` predicate is defined as follows: + +- `rt castable` + - iff not (rt <: (ref null cont)) + ### Execution The same control tag may be used simultaneously by `throw`, `suspend`, @@ -833,6 +1045,120 @@ events and only `(on $e switch)` handlers can handle `switch` events. The handler search continues past handlers for the wrong kind of event, even if they use the correct tag. +#### Store extensions + +* New store component `conts` for allocated continuations + - `S ::= {..., conts ?*}` + +* A continuation is a context + - `continst ::= E` + + +#### Administrative instructions + +* `(ref.cont a)` represents a continuation value, where `a` is a *continuation address* indexing into the store's `conts` component + - `ref.cont a : [] -> [(ref $ct)]` + - iff `S.conts[a] = epsilon \/ S.conts[a] = E` + + iff `E[val^n] : t2*` + + and `(val : t1)^n` + - and `$ct ~~ cont $ft` + - and `$ft ~~ [t1^n] -> [t2*]` + +* `(prompt{*} * end)` represents an active handler + - `(prompt{hdl*}? instr* end) : [] -> [t*]` + - iff `instr* : [] -> [t*]` in the empty context + - and `(hdl : [t*])*` + +#### Handler contexts + +``` +H^ea ::= + _ + val* H^ea instr* + label_n{instr*} H^ea end + frame_n{F} H^ea end + catch{...} H^ea end + prompt{hdl*} H^ea end (iff ea notin hdl*) +``` + + +#### Reduction + +* `S; F; (ref.null t) (cont.new $ct) --> S; F; trap` + +* `S; F; (ref.func fa) (cont.new $ct) --> S'; F; (ref.cont |S.conts|)` + - iff `S' = S with conts += E` + - and `E = _ (invoke fa)` + +* `S; F; (ref.null t) (cont.bind $ct $ct') --> S; F; trap` + +* `S; F; (ref.cont ca) (cont.bind $ct $ct') --> S; F; trap` + - iff `S.conts[ca] = epsilon` + +* `S; F; v^n (ref.cont ca) (cont.bind $ct $ct') --> S'; F; (ref.cont |S.conts|)` + - iff `S.conts[ca] = E'` + - and `$ct ~~ cont [t1*] -> [t2*]` + - and `$ct' ~~ cont [t1'*] -> [t2'*]` + - and `n = |t1*| - |t1'*|` + - and `S' = S with conts[ca] = epsilon with conts += E` + - and `E = E'[v^n _]` + +* `S; F; (ref.null t) (resume $ct hdl*) --> S; F; trap` + +* `S; F; (ref.cont ca) (resume $ct hdl*) --> S; F; trap` + - iff `S.conts[ca] = epsilon` + +* `S; F; v^n (ref.cont ca) (resume $ct hdl*) --> S'; F; prompt{hdl'*} E[v^n] end` + - iff `S.conts[ca] = E` + - and `S' = S with conts[ca] = epsilon` + - and `$ct ~~ cont [t1^n] -> [t2*]` + - and `hdl'* = hdl*[F.module.tags / 0..|F.module.tags|-1]` + +* `S; F; (ref.null t) (resume_throw $ct $e hdl*) --> S; F; trap` + +* `S; F; v^m (ref.cont ca) (resume_throw $ct $e hdl*) --> S'; F; (ref.exn |S.exns|) (ref.const ca) (resume_throw_ref $ct hdl*)` + - iff `ta = F.module.tags[$e]` + - and `S.tags[ta].type ~~ [t1^m] -> [t2*]` + - and `S' = S with exns += {S.tags[ta], v^m}` + +* `S; F; (ref.null t) (resume_throw_ref $ct hdl*) --> S; F; trap` + +* `S; F; (ref.cont ca) (resume_throw_ref $ct hdl*) --> S; F; trap` + - iff `S.conts[ca] = epsilon` + +* `S; F; (ref.null t) (ref.cont ca) (resume_throw_ref $ct hdl*) --> S; F; trap` + - iff `S.conts[ca] = E` + +* `S; F; (ref.exn ea) (ref.cont ca) (resume_throw_ref $ct hdl*) --> S'; F; prompt{hdl'*} E[(ref.exn ea) throw_ref] end` + - iff `S.conts[ca] = E` + - and `S' = S with conts[ca] = epsilon` + - and `hdl'* = hdl*[F.module.tags / 0..|F.module.tags|-1]` + +* `S; F; (prompt{hdl*} v* end) --> S; F; v*` + +* `S; F; (suspend $e) --> S; F; (suspend ea)` + - iff `ea = F.module.tags[$e]` + +* `S; F; (prompt{hdl1* (on ea $l) hdl2*} H^ea[v^n (suspend ea)] end) --> S'; F; v^n (ref.cont |S.conts|) (br $l)` + - iff `forall $l', (on ea $l') notin hdl1*` + - and `S.tags[ea].type ~~ [t1^n] -> [t2^m]` + - and `S' = S with conts += (H^ea : m)` + +* `S; F; (switch $ct $e) --> S; F; (switch $ct ea)` + - iff `ea = F.module.tags[$e]` + +* `S; F; (ref.null t) (switch $ct ea) --> S; F; trap` + +* `S; F; (ref.cont ca) (switch $ct ea) --> S; F; trap` + - iff `S.conts[ca] = epsilon` + +* `S; F; (prompt{hdl1* (on ea switch) hdl2*} H^ea[v^n (ref.cont ca) (switch $ct ea)] end) --> S''; F; prompt{hdl1* (on ea switch) hdl2*} E[v^n (ref.cont |S.conts|)] end` + - iff `S.conts[ca] = E` + - and `(on switch ea) notin hdl1*` + - and `$ct ~~ cont [t1^n (ref $ct2)] -> [t2*]` + - and `S' = S with conts[ca] = epsilon` + - and `S'' = S' with conts += H^ea` + ### Binary format We extend the binary format of composite types, heap types, and instructions. @@ -842,7 +1168,7 @@ We extend the binary format of composite types, heap types, and instructions. | Opcode | Type | Parameters | Note | | ------ | --------------- | ---------- |------| | -0x20 | `func t1* t2*` | `t1* : vec(valtype)` `t2* : vec(valtype)` | from Wasm 1.0 | -| -0x23 | `cont $ft` | `$ft : typeidx` | new | +| -0x23 | `cont $ft` | `$ft : s33` | new | #### Heap Types @@ -856,7 +1182,7 @@ The opcode for heap types is encoded as an `s33`. #### Instructions -We use the use the opcode space `0xe0-0xe5` for the seven new instructions. +We use the use the opcode space `0xe0-0xe6` for the seven new instructions. | Opcode | Instruction | Immediates | | ------ | ------------------------ | ---------- | @@ -865,10 +1191,11 @@ We use the use the opcode space `0xe0-0xe5` for the seven new instructions. | 0xe2 | `suspend $t` | `$t : u32` | | 0xe3 | `resume $ct hdl*` | `$ct : u32` (for hdl see below) | | 0xe4 | `resume_throw $ct $e hdl*` | `$ct : u32`, `$e : u32` (for hdl see below) | -| 0xe5 | `switch $ct1 $e` | `$ct1 : u32`, `$e : u32` | +| 0xe5 | `resume_throw_ref $ct hdl*` | `$ct : u32` (for hdl see below) | +| 0xe6 | `switch $ct1 $t` | `$ct1 : u32`, `$t : u32` | -In the case of `resume` and `resume_throw` we use a leading byte to -indicate the shape of `hdl` as follows. +In the cases of `resume`, `resume_throw`, and `resume_throw_ref` we use a +leading byte to indicate the shape of `hdl` as follows. | Opcode | On clause shape | Immediates | | ------ | --------------- | ---------- | diff --git a/proposals/bag-o-stacks/Explainer.md b/proposals/stack-switching/design-notes/bag-o-stacks/Explainer.md similarity index 100% rename from proposals/bag-o-stacks/Explainer.md rename to proposals/stack-switching/design-notes/bag-o-stacks/Explainer.md diff --git a/proposals/continuations/Explainer.md b/proposals/stack-switching/design-notes/continuations/Explainer.md similarity index 99% rename from proposals/continuations/Explainer.md rename to proposals/stack-switching/design-notes/continuations/Explainer.md index 72f00df79..b0db8a452 100644 --- a/proposals/continuations/Explainer.md +++ b/proposals/stack-switching/design-notes/continuations/Explainer.md @@ -216,7 +216,7 @@ a *handler*, which handles subsequent control suspensions within the continuation. ```wast - resume $ct (tag $e $l)* : [tp* (ref $ct)] -> [tr*] + resume $ct (on $e $l)* : [tp* (ref $ct)] -> [tr*] where: - $ct = cont [tp*] -> [tr*] ``` @@ -440,7 +440,7 @@ We assume a suitable interface to a queue of active threads represented as continuations. The scheduler is a loop which repeatedly runs the continuation (thread) at the head of the queue. It does so by resuming the continuation with a handler for the `$yield` tag. The -handler `(tag $yield $on_yield)` specifies that the `$yield` tag +handler `(on $yield $on_yield)` specifies that the `$yield` tag is handled by running the code immediately following the block labelled with `$on_yield`, the `$on_yield` clause. The result of the block `(result (ref $cont))` declares that there will be a @@ -1439,7 +1439,7 @@ executing a variant of the `resume` instruction and is passed to the continuation: ```wast - resume_with $ht $ct (tag $e $l)* : [ t1* (ref $ht) (ref $ct) ] -> [ t2* ] + resume_with $ht $ct (on $e $l)* : [ t1* (ref $ht) (ref $ct) ] -> [ t2* ] where: - $ht = handler t2* - $ct = cont ([ (ref $ht) t1* ] -> [ t2* ]) @@ -1491,7 +1491,7 @@ instruction for switching directly to another continuation: This behaves as if there was a built-in tag ```wast - (tag $Switch (param t1* (ref $ct1)) (result t3*)) + (on $Switch (param t1* (ref $ct1)) (result t3*)) ``` with which the computation suspends to the handler, and the handler diff --git a/proposals/continuations/Overview.md b/proposals/stack-switching/design-notes/continuations/Overview.md similarity index 83% rename from proposals/continuations/Overview.md rename to proposals/stack-switching/design-notes/continuations/Overview.md index 7b4e5fc09..04a9a8e37 100644 --- a/proposals/continuations/Overview.md +++ b/proposals/stack-switching/design-notes/continuations/Overview.md @@ -31,27 +31,27 @@ Based on [typed reference proposal](https://github.com/WebAssembly/function-refe - `suspend $t : [t1*] -> [t2*]` - iff `tag $t : [t1*] -> [t2*]` -* `resume (tag )*` resumes a continuation - - `resume $ct (tag $t $l)* : [t1* (ref null? $ct)] -> [t2*]` +* `resume (on )*` resumes a continuation + - `resume $ct (on $t $l)* : [t1* (ref null? $ct)] -> [t2*]` - iff `$ct = cont $ft` - and `$ft = [t1*] -> [t2*]` - - and `(tag $t : [te1*] -> [te2*])*` + - and `(on $t : [te1*] -> [te2*])*` - and `(label $l : [te1'* (ref null? $ct')])*` - and `([te1*] <: [te1'*])*` - and `($ct' = cont $ft')*` - - and `$ft' = [t1'*] -> [t2'*]` + - and `($ft' = [t1'*] -> [t2'*])*` - and `([te2*] -> [t2*] <: [t1'*] -> [t2'*])*` -* `resume_throw (tag )` aborts a continuation - - `resume_throw $ct $e (tag $t $l): [te* (ref null? $ct)] -> [t2*]` +* `resume_throw (on )*` aborts a continuation + - `resume_throw $ct $e (on $t $l)* : [te* (ref null? $ct)] -> [t2*]` - iff `(tag $e : [te*] -> [])` - and `$ct = cont $ft` - and `$ft = [t1*] -> [t2*]` - - and `(tag $t : [te1*] -> [te2*])*` + - and `(on $t : [te1*] -> [te2*])*` - and `(label $l : [te1'* (ref null? $ct')])*` - and `([te1*] <: [te1'*])*` - and `($ct' = cont $ft')*` - - and `$ft' = [t1'*] -> [t2'*]` + - and `($ft' = [t1'*] -> [t2'*])*` - and `([te2*] -> [t2*] <: [t1'*] -> [t2'*])*` * `barrier * end` blocks suspension @@ -131,22 +131,22 @@ H^ea ::= - and `S' = S with conts[ca] = epsilon with conts += (E : |t1'*|)` - and `E = E'[v^n _]` -* `S; F; (ref.null t) (resume $ct (tag $e $l)*) --> S; F; trap` +* `S; F; (ref.null t) (resume $ct (on $e $l)*) --> S; F; trap` -* `S; F; (ref.cont ca) (resume $ct (tag $e $l)*) --> S; F; trap` +* `S; F; (ref.cont ca) (resume $ct (on $e $l)*) --> S; F; trap` - iff `S.conts[ca] = epsilon` -* `S; F; v^n (ref.cont ca) (resume $ct (tag $t $l)*) --> S'; F; handle{(ea $l)*} E[v^n] end` +* `S; F; v^n (ref.cont ca) (resume $ct (on $t $l)*) --> S'; F; handle{(ea $l)*} E[v^n] end` - iff `S.conts[ca] = (E : n)` - and `(ea = F.tags[$t])*` - and `S' = S with conts[ca] = epsilon` -* `S; F; (ref.null t) (resume_throw $ct $e (tag $t $l)*) --> S; F; trap` +* `S; F; (ref.null t) (resume_throw $ct $e (on $t $l)*) --> S; F; trap` -* `S; F; (ref.cont ca) (resume_throw $ct $e (tag $t $l)*) --> S; F; trap` +* `S; F; (ref.cont ca) (resume_throw $ct $e (on $t $l)*) --> S; F; trap` - iff `S.conts[ca] = epsilon` -* `S; F; v^m (ref.cont ca) (resume_throw $ct $e (tag $t $l)*) --> S'; F; handle{(ea $l)*} E[v^m (throw $e)] end` +* `S; F; v^m (ref.cont ca) (resume_throw $ct $e (on $t $l)*) --> S'; F; handle{(ea $l)*} E[v^m (throw $e)] end` - iff `S.conts[ca] = (E : n)` - and `(ea = F.tags[$t])*` - and `S.tags[F.tags[$e]].type = [t1^m] -> [t2*]` diff --git a/proposals/stack-switching/design-notes/reified-fibers/Explainer.md b/proposals/stack-switching/design-notes/reified-fibers/Explainer.md new file mode 100644 index 000000000..d042090dc --- /dev/null +++ b/proposals/stack-switching/design-notes/reified-fibers/Explainer.md @@ -0,0 +1,519 @@ +# Reified Fibers + +This document outlines a simplified approach to stack-switching in WebAssembly, replacing the concept of reified continuations with **reified fibers**. This design aims to be more direct and easier to reason about, utilizing explicit fiber types and control flow through blocks. + +## Core Concepts + +### 1. Fiber Types +A fiber represents an independently executing coroutine. A fiber is statically typed with a resume type and a suspend type: +- **Resume Type** (`$t1*`): The types of the arguments provided to the fiber when it is resumed. +- **Suspend Type** (`$t2*`): The types of the values the fiber generates when it suspends execution and yields control. + +In WebAssembly syntax, a fiber type can be defined as: +```wasm +(type $F (fiber (param $t1...) (result $t2...))) +``` + +### 2. Creating Fibers (`fiber.new`) +Fibers are instantiated from functions. A new instruction, `fiber.new`, creates a reified fiber. + +```wasm +fiber.new $func_index +``` + +**Semantics:** +- The target function must have a signature that matches `[ (ref $F), $prefix_args..., $t1... ] -> []`, where `$t1...` are the resume arguments of the fiber type. *(Note: The function must not return any values. When a fiber completes by returning, control falls through to the instruction after `fiber.resume` with an empty stack contribution from the fiber.)* +- `fiber.new` consumes `$prefix_args...` from the current stack. +- It creates the new fiber and pushes its reference `(ref $F)` onto the stack. +- The new fiber's reference `(ref $F)` is automatically prepended to the user-supplied `$prefix_args...`, binding all of them to the function. +- When the fiber is first resumed, it will receive the `$t1...` arguments, and the underlying function will begin executing with the fiber reference, the prefix arguments, and the resume arguments. + +### 3. Resuming Fibers (`fiber.resume`) +The `fiber.resume` instruction transfers control to a fiber. It is designed to integrate seamlessly with WebAssembly's structured control flow, specifically by breaking to a block if the fiber suspends. + +```wasm +block $suspend_handler (result $suspend_args...) + ;; Stack contains: [ ... $resume_args..., (ref $F) ] + fiber.resume $suspend_handler + + ;; Execution continues here if the fiber RETURNS (finishes execution) +end +;; Execution continues here if the fiber SUSPENDS. +;; The stack now contains the $suspend_args... yielded by the fiber. +``` + +**Semantics:** +- `fiber.resume` consumes the resume arguments (`$t1...`) and a reference to the fiber (`ref $F`) from the stack. +- Control is transferred to the fiber. +- **Return:** If the fiber completes its execution (returns from its base function), control returns to the instruction *immediately following* `fiber.resume`. +- **Suspend:** If the fiber suspends, control breaks to the block specified by the label (`$suspend_handler`). The values provided by the suspend instruction (`$suspend_args...`) are pushed onto the stack. *(Note: In this stacked design, when a fiber suspends, it may be a nested fiber suspending to a distant ancestor. Control always returns to the resumer of the target fiber. Since that resumer knows which fiber it explicitly resumed, there is no ambiguity about which fiber's suspension block was triggered, even if the suspension was initiated by a deeply nested child).* + +### 4. Suspending Fibers (`fiber.suspend`) +The `fiber.suspend` instruction allows a fiber to pause its execution and yield values back to its resumer. + +```wasm +;; Stack contains: [ ... $suspend_args..., (ref $F) ] +fiber.suspend +``` + +**Semantics:** +- `fiber.suspend` takes the suspend arguments (`$suspend_args...`) and a reference to a fiber (`ref $F`). +- It pauses the current execution context. +- Control is transferred back to the resumer (breaking to the block label specified in the `fiber.resume` call). +- Only the `$suspend_args...` are passed to the resumer's block. +- When this fiber is subsequently resumed, execution will continue immediately after the `fiber.suspend` instruction, and the new resume arguments will be pushed onto the fiber's stack. + +--- + +## Example: Generator Pattern + +Here is how a simple generator (yielding integers) would look under this design. + +```wasm +;; Fiber type: takes no resume args, yields only an i32 +(type $GenFiber (fiber (param) (result i32))) + +;; The generator function +;; $self is automatically prepended by fiber.new, followed by the prefixed $state +(func $gen_func (param $self (ref $GenFiber)) (param $state i32) + loop $l + ;; Calculate next value (e.g., state + 1) + ... + + ;; Suspend, yielding the value + ;; Stack: [ $value, (ref $GenFiber) ] + local.get $self + fiber.suspend + + br $l + end +) + +(func $consumer + (local $f (ref $GenFiber)) + + ;; Prefix the initial state (0) and create the fiber. + ;; The new fiber's ref is automatically prepended as the first argument to $gen_func. + i32.const 0 + fiber.new $gen_func + local.set $f + + loop $consume_loop + block $suspend_handler (result i32) + ;; Push fiber ref and resume + local.get $f + fiber.resume $suspend_handler + + ;; If we reach here, the generator returned (finished) + return + end + + ;; If we reach here, the generator suspended. + ;; Stack contains: [ value ] + ;; The resumer already knows `$f` is the fiber that suspended. + + ;; Process the yielded i32 value... + ... + + br $consume_loop + end +) +``` + +## Example: Extending the Generator + +In more advanced scenarios, a generator might not only yield values but also receive values from the consumer each time it is resumed. The fiber's explicit `resume_type` makes this bidirectional data flow straightforward. + +```wasm +;; Fiber type: takes an f32 on resume, yields an i32 +(type $EchoGenFiber (fiber (param f32) (result i32))) + +;; The generator function +;; $self is automatically prepended by fiber.new, followed by the prefixed $state +;; $first_resume_val is provided by the very first fiber.resume +(func $echo_gen_func (param $self (ref $EchoGenFiber)) (param $state i32) (param $first_resume_val f32) + (local $resume_val f32) + (local.set $resume_val (local.get $first_resume_val)) + + loop $l + ;; Do something with $resume_val and $state ... + ... + + ;; Suspend, yielding our state + local.get $state + local.get $self + fiber.suspend + + ;; Upon resumption, the new resume argument (f32) is left on the stack + local.set $resume_val + + br $l + end +) +``` + +## Example: Task Scheduling + +This example demonstrates how to implement cooperative task scheduling. The scheduler maintains a queue of tasks. When a task wishes to yield, it suspends itself, returning control to the scheduler loop. + +```wasm +;; Fiber type for tasks: no resume args, yields no args when suspended. +(type $TaskFiber (fiber (param) (result))) + +(module $scheduler + ;; A simple queue for tasks + (table $task_queue 1000 (ref null $TaskFiber)) + + ;; Utility functions for the queue + (func $enqueue (param $f (ref $TaskFiber)) ...) + (func $dequeue (result (ref null $TaskFiber)) ...) + + (func $entry (param $initial_task (ref $TaskFiber)) + (local $next_task (ref null $TaskFiber)) + + ;; Start with the initial task + (local.get $initial_task) + (call $enqueue) + + (loop $scheduler_loop + (call $dequeue) + (local.set $next_task) + + ;; If queue is empty, all tasks are finished + (br_if $scheduler_loop_end (ref.is_null (local.get $next_task))) + + (block $on_yield + ;; Push the fiber reference to resume + (local.get $next_task) + (fiber.resume $on_yield) + + ;; If the task returns, it has completed execution. + ;; We loop around to pick the next task. + (br $scheduler_loop) + ) + + ;; If we reach here, the task yielded (suspended). + ;; The resumer knows exactly which task suspended ($next_task). + ;; Enqueue it to be run again later. + (local.get $next_task) + (call $enqueue) + (br $scheduler_loop) + ) + (label $scheduler_loop_end) + ) + + ;; Example Task + ;; $self is automatically prepended by fiber.new + (func $task_0 (param $self (ref $TaskFiber)) + ... + ;; Yield execution back to the scheduler + local.get $self + fiber.suspend + + ... + ) +) +``` + +## Example: Heterogeneous Stacked Suspension (I/O within a Generator) + +This example demonstrates the composability of stacked fibers: combining use cases can mean needing to suspend a deeper computation than the most recently active one. We have a **Scheduler** managing a **Task**, and that **Task** consumes a **Generator**. + +The **Generator** has two distinct suspension behaviors: +1. **Local Yield:** It yields a produced value to its immediate caller (the Task). +2. **Deep I/O Suspension:** It suspends the entire task back to the **Scheduler** to wait for an I/O event (e.g., a timer), bypassing the Task's internal logic. + +It also demonstrates **bidirectional communication**: the Task can resume the Generator with a sentinel value to either request the next value or cancel the generator. + +```wasm +;; Command constants for Generator control +;; 0: CONTINUE, 1: CANCEL + +;; Fiber type for the Scheduler (handles tasks that might yield Ie /O events) +(type $TaskFiber (fiber (param i32) (result i32))) + +;; Fiber type for the Generator (yields values to the task) +;; Resume: 0 (CONTINUE) or 1 (CANCEL). Result: yielded i32 value. +(type $GenFiber (fiber (param i32) (result i32))) + +(module $heterogeneous_example + ;; ... Scheduler state (queue, event loop) ... + + ;; The Generator function + ;; $initial_cmd is provided by the very first fiber.resume + (func $gen_func (param $gen_self (ref $GenFiber)) (param $task_self (ref $TaskFiber)) (param $initial_cmd i32) + (local $i i32) + (local $cmd i32) + (local.set $cmd (local.get $initial_cmd)) + + (loop $l + ;; Check if the driver requested cancellation (1 = CANCEL) + (i32.eq (local.get $cmd) (i32.const 1)) + (if (then return)) + + ;; 1. Standard local yield to the Task + (local.get $i) + (local.get $gen_self) ;; Target: the generator itself + fiber.suspend + (local.set $cmd) ;; Capture the command for the next iteration + + ;; Check again after resumption (1 = CANCEL) + (i32.eq (local.get $cmd) (i32.const 1)) + (if (then return)) + + ;; 2. Deep suspension to the Scheduler for I/O + (i32.const 100) ;; Argument for scheduler (e.g., sleep duration) + (local.get $task_self) ;; Target: the Scheduler's fiber + fiber.suspend + drop ;; Scheduler resume arguments (e.g. status) are ignored here for simplicity + + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $l) + end) + ) + + ;; The Task function + (func $task_entry (param $task_self (ref $TaskFiber)) (param $initial_resume i32) + (local $gen (ref $GenFiber)) + (local $val i32) + + ;; Create the generator + (local.get $task_self) + (fiber.new $gen_func) + (local.set $gen) + + (loop $consume + (block $on_gen_yield (result i32) + ;; Send CONTINUE (0) sentinel to request next value + (i32.const 0) + (local.get $gen) + (fiber.resume $on_gen_yield) + + ;; If the generator returns, the task is finished + return + end) + (local.set $val) + + ;; If we've seen enough values, cancel the generator (1 = CANCEL) + (i32.gt_s (local.get $val) (i32.const 10)) + (if (then + (i32.const 1) + (local.get $gen) + (fiber.resume $on_gen_yield) ;; This will cause $gen_func to return + return + )) + + (local.get $val) + (call $process_value) + + br $consume + end) + ) +) +``` + + +### Execution Flow: + +1. **Normal Yield:** + * `generator` calls `fiber.suspend` with `gen_self`. + * The system finds `gen_self` is the current fiber. + * Control returns to the **Task's** `$on_gen_yield` block. + * The **Task** processes the value and loops to resume the generator. +2. **I/O Suspension:** + * `generator` calls `fiber.suspend` with `task_self`. + * The system searches the stack: `[Generator -> Task -> Scheduler]`. + * It finds `task_self` (the Task) and suspends the **entire chain** (Generator + Task). + * Control returns to the **Scheduler** (the resumer of the Task). + * The **Scheduler** sees the sleep request, puts the task in a "waiting" queue, and runs other work. + * When the timer expires, the **Scheduler** resumes the **Task**. + * Execution resumes **inside the generator**, immediately after the I/O suspension point. The **Task's** intermediate logic is never even aware that the suspension happened. + +*(Note: In the examples above, the fiber reference is explicitly plumbed through the system. By automatically prepending `(ref $F)` to the fiber's prefix arguments during `fiber.new`, the fiber receives its own reference exactly once when it starts. The fiber can then store it in a local and use it to suspend itself later, eliminating the need to pass it back and forth on every resume).* + +## Specification Changes + +This section provides the formal validation and execution rules for the fiber-based stack-switching extension. + +### 1. Types + +#### Fiber Types +A new heap type `fiber` is added to the set of heap types. A fiber type is defined by a pair of function signatures: the **resume signature** and the **suspend signature**. + +``` +fiber_type ::= fiber (param *) (result *) +``` + +- The `param` types (`t1*`) specify the arguments that must be provided when the fiber is resumed. +- The `result` types (`t2*`) specify the values yielded by the fiber when it suspends. + +#### Reference Types +References to fibers are added as a new kind of reference type: `(ref $F)` or `(ref null $F)`, where `$F` is a fiber type. + +### 2. Instructions + +#### `fiber.new $F $func_idx` +Creates a new fiber of type `$F` from the function `$func_idx`. + +**Validation:** +- Let `$F` be a fiber type with resume signature `[t1*] -> [t2*]`. +- Let `$func` be the function at index `$func_idx`. +- The type of `$func` must be `[(ref $F) tp* t1*] -> []`, where `tp*` are the prefix arguments. +- The instruction has type `[tp*] -> [(ref $F)]`. + +**Execution:** +- Pop the prefix arguments `tp*` from the stack. +- Create a new fiber instance associated with `$func`. +- The captured prefix arguments bound to the fiber's execution context are the new fiber's own reference `(ref $F)` followed by the popped `tp*`. +- Push a reference to the new fiber onto the stack. + +*Formal execution semantics:* +``` +* `S; F; v^n (fiber.new $F $func) --> S'; F; (ref.fiber fa)` + - iff `fa = |S.fibers|` + - and `S' = S with fibers += E` + - and `E = _ (ref.fiber fa) v^n (invoke $func)` +``` + +#### `fiber.resume $L` +Resumes a suspended fiber. If the fiber suspends, control transfers to the block labeled by `$L`. + +**Validation:** +- Let `(ref $F)` be the type on top of the stack, where `$F` is a fiber type with resume signature `[t1*]` and suspend signature `[t2*]`. +- The stack below it must match the resume signature `t1*`. +- The label `$L` must have the type `[t2*]`. +- The instruction has type `[t1* (ref $F)] -> []`. + +**Execution:** +- Pop the fiber reference `f` and the resume arguments `v1*`. +- If `f` is currently active (already running on some stack) or exhausted (has returned), trap. +- Transfer control to the fiber `f`. +- **On Return:** If the fiber completes its execution by returning from its entry function, control returns to the instruction immediately following the `fiber.resume`. +- **On Suspend:** If the fiber executes a `fiber.suspend`, control transfers back to the resumer by breaking to the label `$L`. The suspend values `v2*` are pushed onto the stack. + +#### `fiber.resume_throw $L $tag` +Resumes a fiber by raising a specified exception at its current suspension point. + +**Validation:** +- Let `$tag` be an exception tag with type `[te*] -> []`. +- Let `(ref $F)` be the type on top of the stack, where `$F` is a fiber type with suspend signature `[t2*]`. +- The stack below it must match the exception parameter types `te*`. +- The label `$L` must have the type `[t2*]`. +- The instruction has type `[te* (ref $F)] -> []`. + +**Execution:** +- Pop the fiber reference `f` and the exception arguments `ve*`. +- If `f` is active or exhausted, trap. +- Resume fiber `f`, but instead of pushing resume arguments, immediately raise the exception `$tag` with values `ve*` at the fiber's current suspension point. +- **On Return/Uncaught Exception:** If the exception is not caught within the fiber and causes it to terminate, or if the fiber subsequently returns, the behavior is as if the fiber returned (control falls through `fiber.resume_throw`). +- **On Suspend:** If the fiber catches the exception and subsequently executes `fiber.suspend`, control transfers to the resumer's label `$L` as usual. + +*Formal execution semantics:* +``` +* `S; F; (ref.null $F) (fiber.resume_throw $L $tag) --> S; F; trap` + +* `S; F; v^n (ref.fiber fa) (fiber.resume_throw $L $tag) --> S'; F; label{L} E[v^n throw $tag] end` + - iff `S.fibers[fa] = E` + - and `S' = S with fibers[fa] = epsilon` +``` + +#### `fiber.resume_throw_ref $L` +Resumes a fiber by raising an existing exception (provided as an `exnref`) at its current suspension point. + +**Validation:** +- Let `(ref $F)` be the type on top of the stack, where `$F` is a fiber type. +- The type below it must be `exnref`. +- The label `$L` must have the type `[t2*]`. +- The instruction has type `[exnref (ref $F)] -> []`. + +**Execution:** +- Pop the fiber reference `f` and the exception reference `e`. +- If `f` is active or exhausted, trap. +- Resume fiber `f` and raise the exception `e` at its current suspension point. +- Unwinding and suspension behavior is identical to `fiber.resume_throw`. + +*Formal execution semantics:* +``` +* `S; F; (ref.null exn) (ref.fiber fa) (fiber.resume_throw_ref $L) --> S; F; trap` + +* `S; F; (ref.null $F) (fiber.resume_throw_ref $L) --> S; F; trap` + +* `S; F; (ref.exn ea) (ref.fiber fa) (fiber.resume_throw_ref $L) --> S'; F; label{L} E[(ref.exn ea) throw_ref] end` + - iff `S.fibers[fa] = E` + - and `S' = S with fibers[fa] = epsilon` +``` + +--- + +#### `fiber.suspend` +Suspends the currently executing fiber and yields values to its resumer. + +**Validation:** +- The instruction takes a fiber reference `(ref $F)` from the top of the stack. +- Let `$F` be a fiber type with resume signature `[t1*]` and suspend signature `[t2*]`. +- The stack below the fiber reference must match the suspend signature `t2*`. +- The instruction has type `[t2* (ref $F)] -> [t1*]`. +- *(Note: Statically, this instruction is only valid if it is reachable within a fiber context that can provide resume arguments of type `t1*`).* + +**Execution:** +- Pop the fiber reference `f` and the suspend values `v2*`. +- Search the dynamic chain of active fibers for `f`. The search starts with the currently executing fiber. +- If the current fiber is not `f`, the system moves to the current fiber's **resume parent** (the fiber that called `fiber.resume` to start or resume it) and continues the search. +- This process repeats until either `f` is found or the end of the chain is reached (i.e., there is no resume parent). +- **If `f` is found:** + - The current execution context is paused. + - All fibers in the chain between the current fiber and `f` (inclusive) are suspended. + - Control is transferred back to the **resumer of `f`** (breaking to the label `$L` specified in the `fiber.resume $L` call that most recently activated `f`). + - The suspend values `v2*` are pushed onto the resumer's stack. + - When `f` is subsequently resumed, execution continues immediately after its suspension point. If `f` was the fiber that initiated the suspension via `fiber.suspend`, the resume arguments `v1*` are pushed onto its stack. +- **If `f` is not found:** + - The instruction traps. + +*Formal execution semantics:* +``` +* `S; F; label{L} E[v^m (ref.fiber fa) fiber.suspend] end --> S'; F; v^m (br $L)` + - iff `fa` is in the dynamic chain of active fibers. + - and `S'` is `S` updated such that all fibers in the chain from the current one up to `fa` are suspended (their current execution states are captured). + - and `label{L}` is the label associated with the `fiber.resume` that activated `fa`. + +* `S; F; label{L} E[v^m (ref.fiber fa) fiber.suspend] end --> S; F; trap` + - iff `fa` is not in the dynamic chain of active fibers. +``` + +--- + +## Design Decision: Stacked Fibers + +This proposal adopts a **stacked** semantic for fibers. When a fiber executes `fiber.suspend`, it provides a reference to the target fiber it wishes to suspend. The system then searches the current dynamic chain of active fibers (starting from the currently executing fiber and following the "resume parent" links) to find a match. + +If the target fiber is found in the active chain, the entire sub-stack from the current point up to that fiber is suspended, and control returns to that fiber's resumer. If the target fiber is not found in the dynamic chain, the operation traps. + +This design was chosen to provide greater expressivity: + +* **Composition:** Stacked fibers allow for better composition of libraries. A deeply nested component can yield directly to a distant ancestor (like a scheduler or a top-level event loop) without requiring every intermediate layer to explicitly catch and re-propagate the suspension. +* **Reduced Boilerplate:** It eliminates the need for manual "forwarding" of yield operations through intermediate fiber layers. +* **Flexibility:** It supports more complex control flow patterns, such as multi-level generators or effects that skip intermediate handlers. + +While this introduces a search during suspension, the depth of the fiber stack is typically small in practice, keeping the overhead minimal while significantly improving developer ergonomics and system modularity. + +## Design Consideration: Omission of `fiber.bind` + +The original continuation proposal includes a `cont.bind` instruction, which allows for the partial application of resume arguments to a suspended continuation, creating a new continuation with a modified signature. + +This `reified-fibers` proposal explicitly **omits** a `fiber.bind` instruction. + +The primary reason for this omission is performance. If fibers could have their resume arguments dynamically bound and their signatures changed at runtime, it would severely limit an engine's ability to optimize `fiber.resume`. Specifically, knowing the exact resume signature statically at the `fiber.resume` call site allows the WebAssembly engine to pass resume arguments efficiently using **machine registers**. Allowing dynamic binding forces engines to fall back to slower memory-based or stack-based calling conventions, as the true calling convention of the underlying fiber would become obscured. + +Instead, partial application is supported statically only at fiber creation time via the prefix arguments in `fiber.new`. + +## Trap Handling + +If a fiber traps during its execution, the trap propagates transparently to its resumer. From the perspective of the WebAssembly runtime and the parent executing context, it is as though the `fiber.resume` instruction itself trapped. This ensures that trap handling and unwinding semantics remain consistent and predictable across fiber boundaries. + +--- + +## Advantages of this Design +1. **Clear Types:** The explicit separation of `$resume_type` and `$suspend_type` makes the data flow boundary between parent and child threads statically verifiable. +2. **Structured Control Flow:** By mapping `suspend` to a structured `break` (via block labels in `resume`), this design avoids the need for complex, dynamic exception-handler-like structures (`try_table`, `cont.bind`, etc.). +3. **Simplicity:** The mental model maps directly to coroutines and generators found in higher-level languages. +4. **Stable References:** A fiber is represented by a single reference that remains stable throughout the entire lifetime of a task. This eliminates the need for user code or schedulers to constantly update records or tables of "which continuation is currently active" upon every suspension and resumption, reducing state management overhead. diff --git a/proposals/stack-switching-requirements/requirements.md b/proposals/stack-switching/design-notes/requirements.md similarity index 100% rename from proposals/stack-switching-requirements/requirements.md rename to proposals/stack-switching/design-notes/requirements.md diff --git a/proposals/continuations/examples/actor-lwt.wast b/proposals/stack-switching/examples/actor-lwt.wast similarity index 60% rename from proposals/continuations/examples/actor-lwt.wast rename to proposals/stack-switching/examples/actor-lwt.wast index 60d879591..4fc7dcb6f 100644 --- a/proposals/continuations/examples/actor-lwt.wast +++ b/proposals/stack-switching/examples/actor-lwt.wast @@ -62,228 +62,6 @@ ) (register "chain") -;; queues of threads and mailboxes -(module $queue - (type $func (func)) ;; [] -> [] - (type $cont (cont $func)) ;; cont ([] -> []) - - (func $log (import "spectest" "print_i32") (param i32)) - - ;; table (threads) and memory (mailboxes) as simple queues - (table $queue 0 (ref null $cont)) - (memory 1) - - (tag $too-many-mailboxes) - - (global $qdelta i32 (i32.const 10)) - - (global $qback-k (mut i32) (i32.const 0)) - (global $qfront-k (mut i32) (i32.const 0)) - - (func $queue-empty-k (export "queue-empty") (result i32) - (i32.eq (global.get $qfront-k) (global.get $qback-k)) - ) - - (func $dequeue-k (export "dequeue-k") (result (ref null $cont)) - (local $i i32) - (if (call $queue-empty-k) - (then (return (ref.null $cont))) - ) - (local.set $i (global.get $qfront-k)) - (global.set $qfront-k (i32.add (local.get $i) (i32.const 1))) - (table.get $queue (local.get $i)) - ) - - (func $enqueue-k (export "enqueue-k") (param $k (ref $cont)) - ;; Check if queue is full - (if (i32.eq (global.get $qback-k) (table.size $queue)) - (then - ;; Check if there is enough space in the front to compact - (if (i32.lt_u (global.get $qfront-k) (global.get $qdelta)) - (then - ;; Space is below threshold, grow table instead - (drop (table.grow $queue (ref.null $cont) (global.get $qdelta))) - ) - (else - ;; Enough space, move entries up to head of table - (global.set $qback-k (i32.sub (global.get $qback-k) (global.get $qfront-k))) - (table.copy $queue $queue - (i32.const 0) ;; dest = new front = 0 - (global.get $qfront-k) ;; src = old front - (global.get $qback-k) ;; len = new back = old back - old front - ) - (table.fill $queue ;; null out old entries to avoid leaks - (global.get $qback-k) ;; start = new back - (ref.null $cont) ;; init value - (global.get $qfront-k) ;; len = old front = old front - new front - ) - (global.set $qfront-k (i32.const 0)) - ) - ) - ) - ) - (table.set $queue (global.get $qback-k) (local.get $k)) - (global.set $qback-k (i32.add (global.get $qback-k) (i32.const 1))) - ) - - (global $qback-mb (mut i32) (i32.const 0)) - (global $qfront-mb (mut i32) (i32.const 0)) - - (func $queue-empty-mb (export "queue-empty-mb") (result i32) - (i32.eq (global.get $qfront-mb) (global.get $qback-mb)) - ) - - (func $dequeue-mb (export "dequeue-mb") (result i32) - (local $i i32) - (local $mb i32) - (if (call $queue-empty-mb) - (then (return (i32.const -1))) - ) - (local.set $i (global.get $qfront-mb)) - (global.set $qfront-mb (i32.add (local.get $i) (i32.const 1))) - (local.set $mb (i32.load (i32.mul (local.get $i) (i32.const 4)))) - (return (local.get $mb)) - ) - - (func $enqueue-mb (export "enqueue-mb") (param $mb i32) - ;; Check if queue is full - (if (i32.eq (global.get $qback-mb) (i32.const 16383)) - (then - ;; Check if there is enough space in the front to compact - (if (i32.lt_u (global.get $qfront-mb) (global.get $qdelta)) - (then - ;; Space is below threshold, throw exception - (throw $too-many-mailboxes) - ) - (else - ;; Enough space, move entries up to head of table - (global.set $qback-mb (i32.sub (global.get $qback-mb) (global.get $qfront-mb))) - (memory.copy - (i32.const 0) ;; dest = new front = 0 - (i32.mul (global.get $qfront-mb) (i32.const 4)) ;; src = old front - (i32.mul (global.get $qback-mb) (i32.const 4)) ;; len = new back = old back - old front - ) - (memory.fill ;; null out old entries to avoid leaks - (i32.mul (global.get $qback-mb) (i32.const 4)) ;; start = new back - (i32.const -1) ;; init value - (i32.mul (global.get $qfront-mb) (i32.const 4)) ;; len = old front = old front - new front - ) - (global.set $qfront-mb (i32.const 0)) - ) - ) - ) - ) - (i32.store (i32.mul (global.get $qback-mb) (i32.const 4)) (local.get $mb)) - (global.set $qback-mb (i32.add (global.get $qback-mb) (i32.const 1))) - ) -) -(register "queue") - -(module $mailboxes - ;; Stupid implementation of mailboxes that raises an exception if - ;; there are too many mailboxes or if more than one message is sent - ;; to any given mailbox. - ;; - ;; Sufficient for the simple chain example. - - ;; -1 means empty - - (func $log (import "spectest" "print_i32") (param i32)) - - (tag $too-many-mailboxes) - (tag $too-many-messages) - - (memory 1) - - (global $msize (mut i32) (i32.const 0)) ;; current number of mailboxes - (global $mmax i32 (i32.const 1024)) ;; maximum number of mailboxes - - (func $init (export "init") - (global.set $msize (i32.const 0)) - (memory.fill (i32.const 0) (i32.const -1) (i32.mul (global.get $mmax) (i32.const 4))) - ) - - (func $empty-mb (export "empty-mb") (param $mb i32) (result i32) - (local $offset i32) - (local.set $offset (i32.mul (local.get $mb) (i32.const 4))) - (i32.eq (i32.load (local.get $offset)) (i32.const -1)) - ) - - (func $new-mb (export "new-mb") (result i32) - (local $mb i32) - - (if (i32.ge_u (global.get $msize) (global.get $mmax)) - (then (throw $too-many-mailboxes)) - ) - - (local.set $mb (global.get $msize)) - (global.set $msize (i32.add (global.get $msize) (i32.const 1))) - (return (local.get $mb)) - ) - - (func $send-to-mb (export "send-to-mb") (param $v i32) (param $mb i32) - (local $offset i32) - (local.set $offset (i32.mul (local.get $mb) (i32.const 4))) - (if (call $empty-mb (local.get $mb)) - (then (i32.store (local.get $offset) (local.get $v))) - (else (throw $too-many-messages)) - ) - ) - - (func $recv-from-mb (export "recv-from-mb") (param $mb i32) (result i32) - (local $v i32) - (local $offset i32) - (local.set $offset (i32.mul (local.get $mb) (i32.const 4))) - (local.set $v (i32.load (local.get $offset))) - (i32.store (local.get $offset) (i32.const -1)) - (local.get $v) - ) -) -(register "mailboxes") - - -;; a simple example - pass a message through a chain of actors -(module $chain - (type $func (func)) - (type $cont (cont $func)) - - (type $i-func (func (param i32))) - (type $i-cont (cont $i-func)) - - (tag $self (import "actor" "self") (result i32)) - (tag $spawn (import "actor" "spawn") (param (ref $cont)) (result i32)) - (tag $send (import "actor" "send") (param i32 i32)) - (tag $recv (import "actor" "recv") (result i32)) - - (elem declare func $next) - - (func $log (import "spectest" "print_i32") (param i32)) - - (func $next (param $p i32) - (local $s i32) - (local.set $s (suspend $recv)) - (call $log (i32.const -1)) - (suspend $send (local.get $s) (local.get $p)) - ) - - ;; send the message 42 through a chain of n actors - (func $chain (export "chain") (param $n i32) - (local $p i32) - (local.set $p (suspend $self)) - - (loop $l - (if (i32.eqz (local.get $n)) - (then (suspend $send (i32.const 42) (local.get $p))) - (else (local.set $p (suspend $spawn (cont.bind $i-cont $cont (local.get $p) (cont.new $i-cont (ref.func $next))))) - (local.set $n (i32.sub (local.get $n) (i32.const 1))) - (br $l)) - ) - ) - (call $log (suspend $recv)) - ) -) -(register "chain") - ;; interface to lightweight threads (module $lwt (type $func (func)) @@ -371,7 +149,7 @@ (if (call $queue-empty) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) + (resume $cont (on $yield $on_yield) (on $fork $on_fork) (call $dequeue) ) (br $l) ;; thread terminated @@ -498,10 +276,10 @@ (block $on_spawn (result (ref $cont) (ref $i-cont)) (block $on_send (result i32 i32 (ref $cont)) (block $on_recv (result (ref $i-cont)) - (resume $cont (tag $self $on_self) - (tag $spawn $on_spawn) - (tag $send $on_send) - (tag $recv $on_recv) + (resume $cont (on $self $on_self) + (on $spawn $on_spawn) + (on $send $on_send) + (on $recv $on_recv) (local.get $nextk) ) (return) diff --git a/proposals/continuations/examples/actor.wast b/proposals/stack-switching/examples/actor.wast similarity index 98% rename from proposals/continuations/examples/actor.wast rename to proposals/stack-switching/examples/actor.wast index 151c08d58..61caced26 100644 --- a/proposals/continuations/examples/actor.wast +++ b/proposals/stack-switching/examples/actor.wast @@ -329,10 +329,10 @@ (block $on_spawn (result (ref $cont) (ref $i-cont)) (block $on_send (result i32 i32 (ref $cont)) (block $on_recv (result (ref $i-cont)) - (resume $cont (tag $self $on_self) - (tag $spawn $on_spawn) - (tag $send $on_send) - (tag $recv $on_recv) + (resume $cont (on $self $on_self) + (on $spawn $on_spawn) + (on $send $on_send) + (on $recv $on_recv) (local.get $nextk) ) (local.set $mine (call $dequeue-mb)) diff --git a/proposals/continuations/examples/async-await.wast b/proposals/stack-switching/examples/async-await.wast similarity index 98% rename from proposals/continuations/examples/async-await.wast rename to proposals/stack-switching/examples/async-await.wast index 53570c3bc..da6a06837 100644 --- a/proposals/continuations/examples/async-await.wast +++ b/proposals/stack-switching/examples/async-await.wast @@ -265,10 +265,10 @@ (block $on_fulfill (result i32 i32 (ref $cont)) (block $on_async (result (ref $i-cont) (ref $i-cont)) (block $on_await (result i32 (ref $i-cont)) - (resume $cont (tag $yield $on_yield) - (tag $fulfill $on_fulfill) - (tag $async $on_async) - (tag $await $on_await) + (resume $cont (on $yield $on_yield) + (on $fulfill $on_fulfill) + (on $async $on_async) + (on $await $on_await) (local.get $nextk) ) (local.set $nextk (call $dequeue)) diff --git a/proposals/continuations/examples/control-lwt.wast b/proposals/stack-switching/examples/control-lwt.wast similarity index 99% rename from proposals/continuations/examples/control-lwt.wast rename to proposals/stack-switching/examples/control-lwt.wast index d7c00cb3c..cfd2357db 100644 --- a/proposals/continuations/examples/control-lwt.wast +++ b/proposals/stack-switching/examples/control-lwt.wast @@ -29,7 +29,7 @@ (local $h (ref $cont-cont)) (local $k (ref $cont)) (block $on_control (result (ref $cont-cont) (ref $cont)) - (resume $cont (tag $control $on_control) + (resume $cont (on $control $on_control) (local.get $nextk)) (return) ) ;; $on_control (param (ref $cont-func) (ref $cont)) diff --git a/proposals/continuations/examples/fun-actor-lwt.wast b/proposals/stack-switching/examples/fun-actor-lwt.wast similarity index 96% rename from proposals/continuations/examples/fun-actor-lwt.wast rename to proposals/stack-switching/examples/fun-actor-lwt.wast index a9cfff911..88fe60430 100644 --- a/proposals/continuations/examples/fun-actor-lwt.wast +++ b/proposals/stack-switching/examples/fun-actor-lwt.wast @@ -143,7 +143,7 @@ (if (call $queue-empty) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) + (resume $cont (on $yield $on_yield) (on $fork $on_fork) (call $dequeue) ) (br $l) ;; thread terminated @@ -262,10 +262,10 @@ (block $on_recv (result (ref $i-cont)) ;; this should really be a tail call to the continuation ;; do we need a 'return_resume' operator? - (resume $i-cont (tag $self $on_self) - (tag $spawn $on_spawn) - (tag $send $on_send) - (tag $recv $on_recv) + (resume $i-cont (on $self $on_self) + (on $spawn $on_spawn) + (on $send $on_send) + (on $recv $on_recv) (local.get $res) (local.get $ik) ) (return) @@ -312,10 +312,10 @@ (block $on_recv (result (ref $i-cont)) ;; this should really be a tail call to the continuation ;; do we need a 'return_resume' operator? - (resume $cont (tag $self $on_self) - (tag $spawn $on_spawn) - (tag $send $on_send) - (tag $recv $on_recv) + (resume $cont (on $self $on_self) + (on $spawn $on_spawn) + (on $send $on_send) + (on $recv $on_recv) (local.get $k) ) (return) diff --git a/proposals/continuations/examples/fun-lwt.wast b/proposals/stack-switching/examples/fun-lwt.wast similarity index 96% rename from proposals/continuations/examples/fun-lwt.wast rename to proposals/stack-switching/examples/fun-lwt.wast index 2b57b53df..4d7b2e6e6 100644 --- a/proposals/continuations/examples/fun-lwt.wast +++ b/proposals/stack-switching/examples/fun-lwt.wast @@ -133,8 +133,8 @@ (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) (resume $cont - (tag $yield $on_yield) - (tag $fork $on_fork) + (on $yield $on_yield) + (on $fork $on_fork) (local.get $nextk) ) (return_call $sync (call $dequeue)) @@ -160,8 +160,8 @@ (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) (resume $cont - (tag $yield $on_yield) - (tag $fork $on_fork) + (on $yield $on_yield) + (on $fork $on_fork) (local.get $nextk) ) (return_call $tk (call $dequeue)) @@ -180,8 +180,8 @@ (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) (resume $cont - (tag $yield $on_yield) - (tag $fork $on_fork) + (on $yield $on_yield) + (on $fork $on_fork) (local.get $nextk)) (return_call $kt (call $dequeue)) ) ;; $on_fork (result (ref $cont) (ref $cont)) @@ -197,8 +197,8 @@ (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) (resume $cont - (tag $yield $on_yield) - (tag $fork $on_fork) + (on $yield $on_yield) + (on $fork $on_fork) (local.get $nextk) ) (return_call $ykt (call $dequeue)) @@ -217,7 +217,7 @@ (if (ref.is_null (local.get $nextk)) (then (return))) (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) - (resume $cont (tag $yield $on_yield) (tag $fork $on_fork) (local.get $nextk)) + (resume $cont (on $yield $on_yield) (on $fork $on_fork) (local.get $nextk)) (return_call $ytk (call $dequeue)) ) ;; $on_fork (result (ref $cont) (ref $cont)) (local.set $k) diff --git a/proposals/continuations/examples/fun-pipes.wast b/proposals/stack-switching/examples/fun-pipes.wast similarity index 94% rename from proposals/continuations/examples/fun-pipes.wast rename to proposals/stack-switching/examples/fun-pipes.wast index 4c4008de7..85ca0185c 100644 --- a/proposals/continuations/examples/fun-pipes.wast +++ b/proposals/stack-switching/examples/fun-pipes.wast @@ -10,7 +10,7 @@ (func $piper (param $n i32) (param $p (ref $producer)) (param $c (ref $consumer)) (block $on-receive (result (ref $consumer)) - (resume $consumer (tag $receive $on-receive) (local.get $n) (local.get $c)) + (resume $consumer (on $receive $on-receive) (local.get $n) (local.get $c)) (return) ) ;; receive (local.set $c) @@ -20,7 +20,7 @@ (func $copiper (param $c (ref $consumer)) (param $p (ref $producer)) (local $n i32) (block $on-send (result i32 (ref $producer)) - (resume $producer (tag $send $on-send) (local.get $p)) + (resume $producer (on $send $on-send) (local.get $p)) (return) ) ;; send (local.set $p) diff --git a/proposals/continuations/examples/fun-state.wast b/proposals/stack-switching/examples/fun-state.wast similarity index 92% rename from proposals/continuations/examples/fun-state.wast rename to proposals/stack-switching/examples/fun-state.wast index 440aaedfb..8ffde96be 100644 --- a/proposals/continuations/examples/fun-state.wast +++ b/proposals/stack-switching/examples/fun-state.wast @@ -12,7 +12,7 @@ (func $getting (param $k (ref $gk)) (param $s i32) (result i32) (block $on_get (result (ref $gk)) (block $on_set (result i32 (ref $sk)) - (resume $gk (tag $get $on_get) (tag $set $on_set) + (resume $gk (on $get $on_get) (on $set $on_set) (local.get $s) (local.get $k) ) (return) @@ -26,7 +26,7 @@ (func $setting (param $s i32) (param $k (ref $sk)) (result i32) (block $on_get (result (ref $gk)) (block $on_set (result i32 (ref $sk)) - (resume $sk (tag $get $on_get) (tag $set $on_set) + (resume $sk (on $get $on_get) (on $set $on_set) (local.get $k) ) (return) diff --git a/proposals/stack-switching/examples/generator-extended.wast b/proposals/stack-switching/examples/generator-extended.wast new file mode 100644 index 000000000..a23f7a4bf --- /dev/null +++ b/proposals/stack-switching/examples/generator-extended.wast @@ -0,0 +1,81 @@ +(module $generator + (type $ft0 (func)) + (type $ft1 (func (param i32))) + ;; Type of continuations used by the generator: + ;; No param or result types for $ct0: $generator function has no + ;; parameters or return values. + (type $ct0 (cont $ft0)) + + ;; One param of type i32 for $ct1: An i32 is passed back to the + ;; generator when resuming it, and $generator function has no return + ;; values. + (type $ct1 (cont $ft1)) + + (func $print (import "spectest" "print_i32") (param i32)) + + ;; Tag used to coordinate between generator and consumer: The i32 param + ;; corresponds to the generated values passed to consumer, and the i32 result + ;; corresponds to the value passed from the consumer back to the generator. + (tag $gen (param i32) (result i32)) + + + ;; Simple generator yielding values from 100 down to 1. + ;; If non-zero value received back from consumer, resets counter to 100. + (func $generator + (local $i i32) + (local.set $i (i32.const 100)) + (loop $loop + ;; Suspend execution, pass current value of $i to consumer. + (suspend $gen (local.get $i)) + ;; We now have the flag on the stack given to us by the consumer, telling + ;; us whether to reset the generator or not. + (if (result i32) + (then (i32.const 100)) + (else (i32.sub (local.get $i) (i32.const 1))) + ) + (local.tee $i) + (br_if $loop) + ) + ) + (elem declare func $generator) + + (func $consumer (export "consumer") + ;; The continuation of the generator. + (local $c0 (ref $ct0)) + ;; For temporarily storing the continuation received in handler. + (local $c1 (ref $ct1)) + (local $i i32) + ;; Create continuation executing function $generator. + ;; Execution only starts when resumed for the first time. + (local.set $c0 (cont.new $ct0 (ref.func $generator))) + ;; Just counts how many values we have received so far. + (local.set $i (i32.const 1)) + + (loop $loop + (block $on_gen (result i32 (ref $ct1)) + ;; Resume continuation $c0 + (resume $ct0 (on $gen $on_gen) (local.get $c0)) + ;; $generator returned: no more data + (return) + ) + ;; Generator suspended, stack now contains [i32 (ref $ct0)] + ;; Save continuation to resume it in next iteration + (local.set $c1) + ;; Stack now contains the i32 value yielded by $generator + (call $print) + + ;; Calculate flag to be passed back to generator: + ;; Reset after the 42nd iteration + (i32.eq (local.get $i) (i32.const 42)) + ;; Create continuation of type (ref $ct0) by binding flag value. + (cont.bind $ct1 $ct0 (local.get $c1)) + (local.set $c0) + + (local.tee $i (i32.add (local.get $i) (i32.const 1))) + (br_if $loop) + ) + ) + +) + +(invoke "consumer") diff --git a/proposals/stack-switching/examples/generator.wast b/proposals/stack-switching/examples/generator.wast index 4908a4f06..b846b9d43 100644 --- a/proposals/stack-switching/examples/generator.wast +++ b/proposals/stack-switching/examples/generator.wast @@ -1,44 +1,44 @@ (module $generator (type $ft (func)) - ;; Types of continuations used by the generator: + ;; Type of continuations used by the generator: ;; No need for param or result types: No data data passed back to the ;; generator when resuming it, and $generator function has no return ;; values. (type $ct (cont $ft)) - ;; Tag used to coordinate between generator and consumer: The i32 param - ;; corresponds to the generated values passed; no values passed back from - ;; generator to consumer. - (tag $yield (param i32)) + (func $print (import "spectest" "print_i32") (param i32)) + ;; Tag used to coordinate between generator and consumer: The i32 param + ;; corresponds to the generated values passed to consumer; no values passed + ;; back from generator to consumer. + (tag $gen (param i32)) - (func $print (import "spectest" "print_i32") (param i32)) - ;; Simple generator yielding values from 100 down to 1 + ;; Simple generator yielding values from 100 down to 1. (func $generator (local $i i32) (local.set $i (i32.const 100)) - (loop $l - ;; Suspend execution, pass current value of $i to consumer - (suspend $yield (local.get $i)) - ;; Decrement $i and exit loop once $i reaches 0 + (loop $loop + ;; Suspend execution, pass current value of $i to consumer. + (suspend $gen (local.get $i)) + ;; Decrement $i and exit loop once $i reaches 0. (local.tee $i (i32.sub (local.get $i) (i32.const 1))) - (br_if $l) + (br_if $loop) ) ) (elem declare func $generator) - (func $consumer + (func $consumer (export "consumer") (local $c (ref $ct)) ;; Create continuation executing function $generator. ;; Execution only starts when resumed for the first time. (local.set $c (cont.new $ct (ref.func $generator))) (loop $loop - (block $on_yield (result i32 (ref $ct)) - ;; Resume continuation $c - (resume $ct (on $yield $on_yield) (local.get $c)) - ;; $generator returned: no more data + (block $on_gen (result i32 (ref $ct)) + ;; Resume continuation $c. + (resume $ct (on $gen $on_gen) (local.get $c)) + ;; $generator returned: no more data. (return) ) ;; Generator suspended, stack now contains [i32 (ref $ct)] @@ -52,3 +52,5 @@ ) ) + +(invoke "consumer") diff --git a/proposals/continuations/examples/generators.wast b/proposals/stack-switching/examples/generators.wast similarity index 96% rename from proposals/continuations/examples/generators.wast rename to proposals/stack-switching/examples/generators.wast index a7ce4e057..b4bc8727d 100644 --- a/proposals/continuations/examples/generators.wast +++ b/proposals/stack-switching/examples/generators.wast @@ -50,7 +50,7 @@ (loop $l (block $on_yield (result i32 (ref $cont)) (if (local.get $n) - (then (resume $cont (tag $yield $on_yield) (local.get $k))) + (then (resume $cont (on $yield $on_yield) (local.get $k))) ) (return) ) ;; $on_yield (result i32 (ref $cont)) @@ -67,7 +67,7 @@ (loop $l (block $on_yield (result i32 (ref $cont)) (if (local.get $n) - (then (resume $cont (tag $yield $on_yield) (local.get $k))) + (then (resume $cont (on $yield $on_yield) (local.get $k))) ) (return (local.get $sum)) ) ;; $on_yield (result i32 (ref $cont)) @@ -99,7 +99,7 @@ (local $next_k (ref $cont)) (local $next_v i32) (block $on_yield (result i32 (ref $cont)) - (resume $cont (tag $yield $on_yield) + (resume $cont (on $yield $on_yield) (table.get $active (local.get $g)) ) (return (i32.const -1)) diff --git a/proposals/continuations/examples/lwt.wast b/proposals/stack-switching/examples/lwt.wast similarity index 96% rename from proposals/continuations/examples/lwt.wast rename to proposals/stack-switching/examples/lwt.wast index 65232d5bc..20b2369b5 100644 --- a/proposals/continuations/examples/lwt.wast +++ b/proposals/stack-switching/examples/lwt.wast @@ -135,8 +135,8 @@ (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) (resume $cont - (tag $yield $on_yield) - (tag $fork $on_fork) + (on $yield $on_yield) + (on $fork $on_fork) (local.get $nextk) ) (local.set $nextk (call $dequeue)) @@ -166,8 +166,8 @@ (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) (resume $cont - (tag $yield $on_yield) - (tag $fork $on_fork) + (on $yield $on_yield) + (on $fork $on_fork) (local.get $nextk) ) (local.set $nextk (call $dequeue)) @@ -190,8 +190,8 @@ (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) (resume $cont - (tag $yield $on_yield) - (tag $fork $on_fork) + (on $yield $on_yield) + (on $fork $on_fork) (local.get $nextk) ) (local.set $nextk (call $dequeue)) @@ -214,8 +214,8 @@ (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) (resume $cont - (tag $yield $on_yield) - (tag $fork $on_fork) + (on $yield $on_yield) + (on $fork $on_fork) (local.get $nextk) ) (local.set $nextk (call $dequeue)) @@ -239,8 +239,8 @@ (block $on_yield (result (ref $cont)) (block $on_fork (result (ref $cont) (ref $cont)) (resume $cont - (tag $yield $on_yield) - (tag $fork $on_fork) + (on $yield $on_yield) + (on $fork $on_fork) (local.get $nextk) ) (local.set $nextk (call $dequeue)) diff --git a/proposals/continuations/examples/pipes.wast b/proposals/stack-switching/examples/pipes.wast similarity index 94% rename from proposals/continuations/examples/pipes.wast rename to proposals/stack-switching/examples/pipes.wast index e35817856..393248946 100644 --- a/proposals/continuations/examples/pipes.wast +++ b/proposals/stack-switching/examples/pipes.wast @@ -19,7 +19,7 @@ (if (local.get $consuming) (then (block $on-receive (result (ref $consumer)) - (resume $consumer (tag $receive $on-receive) (local.get $n) (local.get $c)) + (resume $consumer (on $receive $on-receive) (local.get $n) (local.get $c)) (return) ) ;; receive (local.set $c) @@ -28,7 +28,7 @@ ) ) ;; else producing (block $on-send (result i32 (ref $producer)) - (resume $producer (tag $send $on-send) (local.get $p)) + (resume $producer (on $send $on-send) (local.get $p)) (return) ) ;; send (local.set $p) diff --git a/proposals/stack-switching/examples/scheduler2-throw.wast b/proposals/stack-switching/examples/scheduler2-throw.wast new file mode 100644 index 000000000..8a6f5b91c --- /dev/null +++ b/proposals/stack-switching/examples/scheduler2-throw.wast @@ -0,0 +1,223 @@ +;; queue of threads +(module $queue + (rec + (type $ft (func (param (ref null $ct)))) + (type $ct (cont $ft))) + + ;; Table as simple queue (keeping it simple, no ring buffer) + (table $task_queue 0 (ref null $ct)) + (global $qdelta i32 (i32.const 10)) + (global $qback (mut i32) (i32.const 0)) + (global $qfront (mut i32) (i32.const 0)) + + (func $queue_empty (export "queue-empty") (result i32) + (i32.eq (global.get $qfront) (global.get $qback)) + ) + + (func $queue_count (export "queue-count") (result i32) + (i32.sub (global.get $qback) (global.get $qfront)) + ) + + (func $dequeue (export "dequeue") (result (ref null $ct)) + (local $i i32) + (if (call $queue_empty) + (then (return (ref.null $ct))) + ) + (local.set $i (global.get $qfront)) + (global.set $qfront (i32.add (local.get $i) (i32.const 1))) + (table.get $task_queue (local.get $i)) + ) + + (func $enqueue (export "enqueue") (param $k (ref null $ct)) + ;; Check if queue is full + (if (i32.eq (global.get $qback) (table.size $task_queue)) + (then + ;; Check if there is enough space in the front to compact + (if (i32.lt_u (global.get $qfront) (global.get $qdelta)) + (then + ;; Space is below threshold, grow table instead + (drop (table.grow $task_queue (ref.null $ct) (global.get $qdelta))) + ) + (else + ;; Enough space, move entries up to head of table + (global.set $qback (i32.sub (global.get $qback) (global.get $qfront))) + (table.copy $task_queue $task_queue + (i32.const 0) ;; dest = new front = 0 + (global.get $qfront) ;; src = old front + (global.get $qback) ;; len = new back = old back - old front + ) + (table.fill $task_queue ;; null out old entries to avoid leaks + (global.get $qback) ;; start = new back + (ref.null $ct) ;; init value + (global.get $qfront) ;; len = old front = old front - new front + ) + (global.set $qfront (i32.const 0)) + ) + ) + ) + ) + (table.set $task_queue (global.get $qback) (local.get $k)) + (global.set $qback (i32.add (global.get $qback) (i32.const 1))) + ) +) +(register "queue") + +(module $scheduler2-switch + (rec + (type $ft (func (param (ref null $ct)))) + ;; Continuation type of all tasks + (type $ct (cont $ft)) + ) + + (func $task_enqueue (import "queue" "enqueue") (param (ref null $ct))) + (func $task_dequeue (import "queue" "dequeue") (result (ref null $ct))) + (func $task_queue-empty (import "queue" "queue-empty") (result i32)) + (func $task_queue-count (import "queue" "queue-count") (result i32)) + (func $print_i32 (import "spectest" "print_i32") (param i32)) + + (global $concurrent_task_limit i32 (i32.const 2)) + + ;; Tag used to yield execution in one task and resume another one. + (tag $yield) + ;; Tag used to abort execution of a task. + (tag $abort) + + (func $schedule_task (param $c (ref null $ct)) + ;; If the task queue is too long, cancel a task in the queue + (if (i32.ge_s (call $task_queue-count) (global.get $concurrent_task_limit)) + (then + (block $exc_handler + (try_table (catch $abort $exc_handler) + (resume_throw $ct $abort (call $task_dequeue)) + ) + ) + ) + ) + (call $task_enqueue (local.get $c)) + ) + + ;; Entry point, becomes parent of all tasks. + ;; Only acts as scheduler when tasks finish. + (func $entry (param $initial_task (ref $ft)) + (local $next_task (ref null $ct)) + + ;; initialise $task_queue with initial task + (call $schedule_task (cont.new $ct (local.get $initial_task))) + + (loop $resume_next + ;; pick $next_task from queue, or return if no more tasks. + ;; Note that there is no suspend handler for $yield + (if (call $task_queue-empty) + (then (return)) + (else (local.set $next_task (call $task_dequeue))) + ) + (resume $ct (on $yield switch) + (ref.null $ct) (local.get $next_task)) + ;; task finished execution: loop to pick next one + (br $resume_next) + ) + ) + + ;; To simplify the example, all task_i functions execute this function. Each + ;; task has an $id, but this is only used for printing. + ;; $to_spawn represents another task that this function will add to the task + ;; queue, unless the reference is null. + ;; $c corresponds to the continuation parameter of the original $task_i + ;; functions. + ;; This means that it is the previous continuation we just switch-ed away + ;; from, or a null reference if the task was resumed from $entry. + (func $task_impl + (param $id i32) + (param $to_spawn (ref null $ft)) + (param $c (ref null $ct)) + + (if (ref.is_null (local.get $c)) + (then) + (else (call $schedule_task (local.get $c)))) + + (if (ref.is_null (local.get $to_spawn)) + (then) + (else (call $schedule_task (cont.new $ct (local.get $to_spawn))))) + + (call $print_i32 (local.get $id)) + (call $yield_to_next) + (call $print_i32 (local.get $id)) + (call $yield_to_next) + (call $print_i32 (local.get $id)) + ) + + ;; The actual $task_i functions simply call $task_impl, with i as the value + ;; for $id, and $task_(i+1) as the task to spawn, except for $task_3, which + ;; does not spawn another task. + ;; + ;; The observant reader may note that all $task_i functions may be seen as + ;; partial applications of $task_impl. + ;; Indeed, we could obtain *continuations* running each $task_i from a + ;; continuation running $task_impl and cont.bind. + + (func $task_3 (type $ft) + (i32.const 3) + (ref.null $ft) + (local.get 0) + (call $task_impl) + ) + (elem declare func $task_3) + + (func $task_2 (type $ft) + (i32.const 2) + (ref.func $task_3) + (local.get 0) + (call $task_impl) + ) + (elem declare func $task_2) + + (func $task_1 (type $ft) + (i32.const 1) + (ref.func $task_2) + (local.get 0) + (call $task_impl) + ) + (elem declare func $task_1) + + (func $task_0 (type $ft) + (i32.const 0) + (ref.func $task_1) + (local.get 0) + (call $task_impl) + ) + (elem declare func $task_0) + + + ;; Determines next task to switch to directly. + (func $yield_to_next + (local $next_task (ref null $ct)) + (local $received_task (ref null $ct)) + + ;; determine $next_task + (local.set $next_task (call $task_dequeue)) + + (block $done + (br_if $done (ref.is_null (local.get $next_task))) + ;; Switch to $next_task. + ;; The switch instruction implicitly passes a reference to the currently + ;; executing continuation as an argument to $next_task. + (switch $ct $yield (local.get $next_task)) + ;; If we get here, some other continuation switch-ed directly to us, or + ;; $entry resumed us. + ;; In the first case, we receive the continuation that switched to us here + ;; and we need to enqueue it in the task list. + ;; In the second case, the passed continuation reference will be null. + (local.set $received_task) + (if (ref.is_null (local.get $received_task)) + (then) + (else (call $schedule_task (local.get $received_task)))) + ) + ;; Just return if no other task in queue, making the $yield_to_next call + ;; a noop. + ) + + (func (export "main") + (call $entry (ref.func $task_0)) + ) +) +(invoke "main") diff --git a/proposals/continuations/examples/static-lwt.wast b/proposals/stack-switching/examples/static-lwt.wast similarity index 98% rename from proposals/continuations/examples/static-lwt.wast rename to proposals/stack-switching/examples/static-lwt.wast index 22bd0f34d..ee7252368 100644 --- a/proposals/continuations/examples/static-lwt.wast +++ b/proposals/stack-switching/examples/static-lwt.wast @@ -110,7 +110,7 @@ (loop $l (if (call $queue-empty) (then (return))) (block $on_yield (result (ref $cont)) - (resume $cont (tag $yield $on_yield) + (resume $cont (on $yield $on_yield) (call $dequeue) ) (br $l) ;; thread terminated diff --git a/test/build.py b/test/build.py index 69c1b19a2..94e7304db 100755 --- a/test/build.py +++ b/test/build.py @@ -13,6 +13,9 @@ WASM_EXEC = os.path.join(INTERPRETER_DIR, 'wasm') WAST_TESTS_DIR = os.path.join(SCRIPT_DIR, 'core') +WAST_TEST_SUBDIRS = [os.path.basename(d) for d in + filter(os.path.isdir, + glob.glob(os.path.join(WAST_TESTS_DIR, '*')))] HARNESS_DIR = os.path.join(SCRIPT_DIR, 'harness') HARNESS_FILES = ['testharness.js', 'testharnessreport.js', 'testharness.css'] @@ -67,13 +70,17 @@ def convert_wast_to_js(out_js_dir): inputs = [] - for wast_file in glob.glob(os.path.join(WAST_TESTS_DIR, '*.wast')): + for wast_file in glob.glob(os.path.join(WAST_TESTS_DIR, '**/*.wast'), + recursive = True): # Don't try to compile tests that are supposed to fail. if '.fail.' in wast_file: continue + js_subdir = os.path.basename(os.path.dirname(wast_file)) + if js_subdir == 'core': + js_subdir = '' js_filename = os.path.basename(wast_file) + '.js' - js_file = os.path.join(out_js_dir, js_filename) + js_file = os.path.join(out_js_dir, js_subdir, js_filename) inputs.append((wast_file, js_file)) pool = mp.Pool(processes=8) @@ -112,9 +119,9 @@ def build_js(out_js_dir): - - - + + +
""" @@ -137,6 +144,8 @@ def wrap_single_test(js_file): def build_html_js(out_dir): ensure_empty_dir(out_dir) + for d in WAST_TEST_SUBDIRS: + ensure_empty_dir(os.path.join(out_dir, d)) copy_harness_files(out_dir, True) tests = convert_wast_to_js(out_dir) @@ -146,19 +155,25 @@ def build_html_js(out_dir): def build_html_from_js(tests, html_dir, use_sync): for js_file in tests: - js_filename = os.path.basename(js_file) - html_filename = js_filename + '.html' - html_file = os.path.join(html_dir, html_filename) + subdir = os.path.basename(os.path.dirname(js_file)) + js_prefix = '../js' + if subdir == 'js': + subdir = '' + js_prefix = './js' + js_filename = os.path.join(js_prefix, subdir, os.path.basename(js_file)) + html_filename = os.path.basename(js_file) + '.html' + html_file = os.path.join(html_dir, subdir, html_filename) js_harness = "sync_index.js" if use_sync else "async_index.js" + harness_dir = os.path.join(js_prefix, 'harness') with open(html_file, 'w+') as f: - content = HTML_HEADER.replace('{PREFIX}', './js/harness') \ - .replace('{WPT_PREFIX}', './js/harness') \ + content = HTML_HEADER.replace('{PREFIX}', harness_dir) \ + .replace('{WPT_PREFIX}', harness_dir) \ .replace('{JS_HARNESS}', js_harness) - content += " ".replace('{SCRIPT}', js_filename) + content += ' ' content += HTML_BOTTOM f.write(content) -def build_html(html_dir, js_dir, use_sync): +def build_html(html_dir, use_sync): print("Building HTML tests...") js_html_dir = os.path.join(html_dir, 'js') @@ -248,11 +263,15 @@ def process_args(): if js_dir is not None: ensure_empty_dir(js_dir) + for d in WAST_TEST_SUBDIRS: + ensure_empty_dir(os.path.join(js_dir, d)) build_js(js_dir) if html_dir is not None: ensure_empty_dir(html_dir) - build_html(html_dir, js_dir, args.use_sync) + for d in WAST_TEST_SUBDIRS: + ensure_empty_dir(os.path.join(html_dir, d)) + build_html(html_dir, args.use_sync) if front_dir is not None: ensure_empty_dir(front_dir) diff --git a/test/core/address.wast b/test/core/address.wast index 320ea36bc..8e52030ec 100644 --- a/test/core/address.wast +++ b/test/core/address.wast @@ -210,12 +210,12 @@ (assert_trap (invoke "16s_bad" (i32.const 1)) "out of bounds memory access") (assert_trap (invoke "32_bad" (i32.const 1)) "out of bounds memory access") -(assert_malformed +(assert_invalid (module quote "(memory 1)" "(func (drop (i32.load offset=4294967296 (i32.const 0))))" ) - "i32 constant" + "offset out of range" ) ;; Load i64 data with different offset/align arguments diff --git a/test/core/address64.wast b/test/core/address64.wast new file mode 100644 index 000000000..29771ae77 --- /dev/null +++ b/test/core/address64.wast @@ -0,0 +1,582 @@ +;; Load i32 data with different offset/align arguments + +(module + (memory i64 1) + (data (i64.const 0) "abcdefghijklmnopqrstuvwxyz") + + (func (export "8u_good1") (param $i i64) (result i32) + (i32.load8_u offset=0 (local.get $i)) ;; 97 'a' + ) + (func (export "8u_good2") (param $i i64) (result i32) + (i32.load8_u align=1 (local.get $i)) ;; 97 'a' + ) + (func (export "8u_good3") (param $i i64) (result i32) + (i32.load8_u offset=1 align=1 (local.get $i)) ;; 98 'b' + ) + (func (export "8u_good4") (param $i i64) (result i32) + (i32.load8_u offset=2 align=1 (local.get $i)) ;; 99 'c' + ) + (func (export "8u_good5") (param $i i64) (result i32) + (i32.load8_u offset=25 align=1 (local.get $i)) ;; 122 'z' + ) + + (func (export "8s_good1") (param $i i64) (result i32) + (i32.load8_s offset=0 (local.get $i)) ;; 97 'a' + ) + (func (export "8s_good2") (param $i i64) (result i32) + (i32.load8_s align=1 (local.get $i)) ;; 97 'a' + ) + (func (export "8s_good3") (param $i i64) (result i32) + (i32.load8_s offset=1 align=1 (local.get $i)) ;; 98 'b' + ) + (func (export "8s_good4") (param $i i64) (result i32) + (i32.load8_s offset=2 align=1 (local.get $i)) ;; 99 'c' + ) + (func (export "8s_good5") (param $i i64) (result i32) + (i32.load8_s offset=25 align=1 (local.get $i)) ;; 122 'z' + ) + + (func (export "16u_good1") (param $i i64) (result i32) + (i32.load16_u offset=0 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16u_good2") (param $i i64) (result i32) + (i32.load16_u align=1 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16u_good3") (param $i i64) (result i32) + (i32.load16_u offset=1 align=1 (local.get $i)) ;; 25442 'bc' + ) + (func (export "16u_good4") (param $i i64) (result i32) + (i32.load16_u offset=2 align=2 (local.get $i)) ;; 25699 'cd' + ) + (func (export "16u_good5") (param $i i64) (result i32) + (i32.load16_u offset=25 align=2 (local.get $i)) ;; 122 'z\0' + ) + + (func (export "16s_good1") (param $i i64) (result i32) + (i32.load16_s offset=0 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16s_good2") (param $i i64) (result i32) + (i32.load16_s align=1 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16s_good3") (param $i i64) (result i32) + (i32.load16_s offset=1 align=1 (local.get $i)) ;; 25442 'bc' + ) + (func (export "16s_good4") (param $i i64) (result i32) + (i32.load16_s offset=2 align=2 (local.get $i)) ;; 25699 'cd' + ) + (func (export "16s_good5") (param $i i64) (result i32) + (i32.load16_s offset=25 align=2 (local.get $i)) ;; 122 'z\0' + ) + + (func (export "32_good1") (param $i i64) (result i32) + (i32.load offset=0 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32_good2") (param $i i64) (result i32) + (i32.load align=1 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32_good3") (param $i i64) (result i32) + (i32.load offset=1 align=1 (local.get $i)) ;; 1701077858 'bcde' + ) + (func (export "32_good4") (param $i i64) (result i32) + (i32.load offset=2 align=2 (local.get $i)) ;; 1717920867 'cdef' + ) + (func (export "32_good5") (param $i i64) (result i32) + (i32.load offset=25 align=4 (local.get $i)) ;; 122 'z\0\0\0' + ) + + (func (export "8u_bad") (param $i i64) + (drop (i32.load8_u offset=4294967295 (local.get $i))) + ) + (func (export "8s_bad") (param $i i64) + (drop (i32.load8_s offset=4294967295 (local.get $i))) + ) + (func (export "16u_bad") (param $i i64) + (drop (i32.load16_u offset=4294967295 (local.get $i))) + ) + (func (export "16s_bad") (param $i i64) + (drop (i32.load16_s offset=4294967295 (local.get $i))) + ) + (func (export "32_bad") (param $i i64) + (drop (i32.load offset=4294967295 (local.get $i))) + ) +) + +(assert_return (invoke "8u_good1" (i64.const 0)) (i32.const 97)) +(assert_return (invoke "8u_good2" (i64.const 0)) (i32.const 97)) +(assert_return (invoke "8u_good3" (i64.const 0)) (i32.const 98)) +(assert_return (invoke "8u_good4" (i64.const 0)) (i32.const 99)) +(assert_return (invoke "8u_good5" (i64.const 0)) (i32.const 122)) + +(assert_return (invoke "8s_good1" (i64.const 0)) (i32.const 97)) +(assert_return (invoke "8s_good2" (i64.const 0)) (i32.const 97)) +(assert_return (invoke "8s_good3" (i64.const 0)) (i32.const 98)) +(assert_return (invoke "8s_good4" (i64.const 0)) (i32.const 99)) +(assert_return (invoke "8s_good5" (i64.const 0)) (i32.const 122)) + +(assert_return (invoke "16u_good1" (i64.const 0)) (i32.const 25185)) +(assert_return (invoke "16u_good2" (i64.const 0)) (i32.const 25185)) +(assert_return (invoke "16u_good3" (i64.const 0)) (i32.const 25442)) +(assert_return (invoke "16u_good4" (i64.const 0)) (i32.const 25699)) +(assert_return (invoke "16u_good5" (i64.const 0)) (i32.const 122)) + +(assert_return (invoke "16s_good1" (i64.const 0)) (i32.const 25185)) +(assert_return (invoke "16s_good2" (i64.const 0)) (i32.const 25185)) +(assert_return (invoke "16s_good3" (i64.const 0)) (i32.const 25442)) +(assert_return (invoke "16s_good4" (i64.const 0)) (i32.const 25699)) +(assert_return (invoke "16s_good5" (i64.const 0)) (i32.const 122)) + +(assert_return (invoke "32_good1" (i64.const 0)) (i32.const 1684234849)) +(assert_return (invoke "32_good2" (i64.const 0)) (i32.const 1684234849)) +(assert_return (invoke "32_good3" (i64.const 0)) (i32.const 1701077858)) +(assert_return (invoke "32_good4" (i64.const 0)) (i32.const 1717920867)) +(assert_return (invoke "32_good5" (i64.const 0)) (i32.const 122)) + +(assert_return (invoke "8u_good1" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "8u_good2" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "8u_good3" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "8u_good4" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "8u_good5" (i64.const 65507)) (i32.const 0)) + +(assert_return (invoke "8s_good1" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "8s_good2" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "8s_good3" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "8s_good4" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "8s_good5" (i64.const 65507)) (i32.const 0)) + +(assert_return (invoke "16u_good1" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "16u_good2" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "16u_good3" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "16u_good4" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "16u_good5" (i64.const 65507)) (i32.const 0)) + +(assert_return (invoke "16s_good1" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "16s_good2" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "16s_good3" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "16s_good4" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "16s_good5" (i64.const 65507)) (i32.const 0)) + +(assert_return (invoke "32_good1" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "32_good2" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "32_good3" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "32_good4" (i64.const 65507)) (i32.const 0)) +(assert_return (invoke "32_good5" (i64.const 65507)) (i32.const 0)) + +(assert_return (invoke "8u_good1" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "8u_good2" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "8u_good3" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "8u_good4" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "8u_good5" (i64.const 65508)) (i32.const 0)) + +(assert_return (invoke "8s_good1" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "8s_good2" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "8s_good3" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "8s_good4" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "8s_good5" (i64.const 65508)) (i32.const 0)) + +(assert_return (invoke "16u_good1" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "16u_good2" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "16u_good3" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "16u_good4" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "16u_good5" (i64.const 65508)) (i32.const 0)) + +(assert_return (invoke "16s_good1" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "16s_good2" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "16s_good3" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "16s_good4" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "16s_good5" (i64.const 65508)) (i32.const 0)) + +(assert_return (invoke "32_good1" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "32_good2" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "32_good3" (i64.const 65508)) (i32.const 0)) +(assert_return (invoke "32_good4" (i64.const 65508)) (i32.const 0)) +(assert_trap (invoke "32_good5" (i64.const 65508)) "out of bounds memory access") + +(assert_trap (invoke "8u_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "8s_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "16u_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "16s_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "32_bad" (i64.const 0)) "out of bounds memory access") + +(assert_trap (invoke "8u_bad" (i64.const 1)) "out of bounds memory access") +(assert_trap (invoke "8s_bad" (i64.const 1)) "out of bounds memory access") +(assert_trap (invoke "16u_bad" (i64.const 1)) "out of bounds memory access") +(assert_trap (invoke "16s_bad" (i64.const 1)) "out of bounds memory access") +(assert_trap (invoke "32_bad" (i64.const 1)) "out of bounds memory access") + + +;; Load i64 data with different offset/align arguments + +(module + (memory i64 1) + (data (i64.const 0) "abcdefghijklmnopqrstuvwxyz") + + (func (export "8u_good1") (param $i i64) (result i64) + (i64.load8_u offset=0 (local.get $i)) ;; 97 'a' + ) + (func (export "8u_good2") (param $i i64) (result i64) + (i64.load8_u align=1 (local.get $i)) ;; 97 'a' + ) + (func (export "8u_good3") (param $i i64) (result i64) + (i64.load8_u offset=1 align=1 (local.get $i)) ;; 98 'b' + ) + (func (export "8u_good4") (param $i i64) (result i64) + (i64.load8_u offset=2 align=1 (local.get $i)) ;; 99 'c' + ) + (func (export "8u_good5") (param $i i64) (result i64) + (i64.load8_u offset=25 align=1 (local.get $i)) ;; 122 'z' + ) + + (func (export "8s_good1") (param $i i64) (result i64) + (i64.load8_s offset=0 (local.get $i)) ;; 97 'a' + ) + (func (export "8s_good2") (param $i i64) (result i64) + (i64.load8_s align=1 (local.get $i)) ;; 97 'a' + ) + (func (export "8s_good3") (param $i i64) (result i64) + (i64.load8_s offset=1 align=1 (local.get $i)) ;; 98 'b' + ) + (func (export "8s_good4") (param $i i64) (result i64) + (i64.load8_s offset=2 align=1 (local.get $i)) ;; 99 'c' + ) + (func (export "8s_good5") (param $i i64) (result i64) + (i64.load8_s offset=25 align=1 (local.get $i)) ;; 122 'z' + ) + + (func (export "16u_good1") (param $i i64) (result i64) + (i64.load16_u offset=0 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16u_good2") (param $i i64) (result i64) + (i64.load16_u align=1 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16u_good3") (param $i i64) (result i64) + (i64.load16_u offset=1 align=1 (local.get $i)) ;; 25442 'bc' + ) + (func (export "16u_good4") (param $i i64) (result i64) + (i64.load16_u offset=2 align=2 (local.get $i)) ;; 25699 'cd' + ) + (func (export "16u_good5") (param $i i64) (result i64) + (i64.load16_u offset=25 align=2 (local.get $i)) ;; 122 'z\0' + ) + + (func (export "16s_good1") (param $i i64) (result i64) + (i64.load16_s offset=0 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16s_good2") (param $i i64) (result i64) + (i64.load16_s align=1 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16s_good3") (param $i i64) (result i64) + (i64.load16_s offset=1 align=1 (local.get $i)) ;; 25442 'bc' + ) + (func (export "16s_good4") (param $i i64) (result i64) + (i64.load16_s offset=2 align=2 (local.get $i)) ;; 25699 'cd' + ) + (func (export "16s_good5") (param $i i64) (result i64) + (i64.load16_s offset=25 align=2 (local.get $i)) ;; 122 'z\0' + ) + + (func (export "32u_good1") (param $i i64) (result i64) + (i64.load32_u offset=0 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32u_good2") (param $i i64) (result i64) + (i64.load32_u align=1 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32u_good3") (param $i i64) (result i64) + (i64.load32_u offset=1 align=1 (local.get $i)) ;; 1701077858 'bcde' + ) + (func (export "32u_good4") (param $i i64) (result i64) + (i64.load32_u offset=2 align=2 (local.get $i)) ;; 1717920867 'cdef' + ) + (func (export "32u_good5") (param $i i64) (result i64) + (i64.load32_u offset=25 align=4 (local.get $i)) ;; 122 'z\0\0\0' + ) + + (func (export "32s_good1") (param $i i64) (result i64) + (i64.load32_s offset=0 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32s_good2") (param $i i64) (result i64) + (i64.load32_s align=1 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32s_good3") (param $i i64) (result i64) + (i64.load32_s offset=1 align=1 (local.get $i)) ;; 1701077858 'bcde' + ) + (func (export "32s_good4") (param $i i64) (result i64) + (i64.load32_s offset=2 align=2 (local.get $i)) ;; 1717920867 'cdef' + ) + (func (export "32s_good5") (param $i i64) (result i64) + (i64.load32_s offset=25 align=4 (local.get $i)) ;; 122 'z\0\0\0' + ) + + (func (export "64_good1") (param $i i64) (result i64) + (i64.load offset=0 (local.get $i)) ;; 0x6867666564636261 'abcdefgh' + ) + (func (export "64_good2") (param $i i64) (result i64) + (i64.load align=1 (local.get $i)) ;; 0x6867666564636261 'abcdefgh' + ) + (func (export "64_good3") (param $i i64) (result i64) + (i64.load offset=1 align=1 (local.get $i)) ;; 0x6968676665646362 'bcdefghi' + ) + (func (export "64_good4") (param $i i64) (result i64) + (i64.load offset=2 align=2 (local.get $i)) ;; 0x6a69686766656463 'cdefghij' + ) + (func (export "64_good5") (param $i i64) (result i64) + (i64.load offset=25 align=8 (local.get $i)) ;; 122 'z\0\0\0\0\0\0\0' + ) + + (func (export "8u_bad") (param $i i64) + (drop (i64.load8_u offset=4294967295 (local.get $i))) + ) + (func (export "8s_bad") (param $i i64) + (drop (i64.load8_s offset=4294967295 (local.get $i))) + ) + (func (export "16u_bad") (param $i i64) + (drop (i64.load16_u offset=4294967295 (local.get $i))) + ) + (func (export "16s_bad") (param $i i64) + (drop (i64.load16_s offset=4294967295 (local.get $i))) + ) + (func (export "32u_bad") (param $i i64) + (drop (i64.load32_u offset=4294967295 (local.get $i))) + ) + (func (export "32s_bad") (param $i i64) + (drop (i64.load32_s offset=4294967295 (local.get $i))) + ) + (func (export "64_bad") (param $i i64) + (drop (i64.load offset=4294967295 (local.get $i))) + ) +) + +(assert_return (invoke "8u_good1" (i64.const 0)) (i64.const 97)) +(assert_return (invoke "8u_good2" (i64.const 0)) (i64.const 97)) +(assert_return (invoke "8u_good3" (i64.const 0)) (i64.const 98)) +(assert_return (invoke "8u_good4" (i64.const 0)) (i64.const 99)) +(assert_return (invoke "8u_good5" (i64.const 0)) (i64.const 122)) + +(assert_return (invoke "8s_good1" (i64.const 0)) (i64.const 97)) +(assert_return (invoke "8s_good2" (i64.const 0)) (i64.const 97)) +(assert_return (invoke "8s_good3" (i64.const 0)) (i64.const 98)) +(assert_return (invoke "8s_good4" (i64.const 0)) (i64.const 99)) +(assert_return (invoke "8s_good5" (i64.const 0)) (i64.const 122)) + +(assert_return (invoke "16u_good1" (i64.const 0)) (i64.const 25185)) +(assert_return (invoke "16u_good2" (i64.const 0)) (i64.const 25185)) +(assert_return (invoke "16u_good3" (i64.const 0)) (i64.const 25442)) +(assert_return (invoke "16u_good4" (i64.const 0)) (i64.const 25699)) +(assert_return (invoke "16u_good5" (i64.const 0)) (i64.const 122)) + +(assert_return (invoke "16s_good1" (i64.const 0)) (i64.const 25185)) +(assert_return (invoke "16s_good2" (i64.const 0)) (i64.const 25185)) +(assert_return (invoke "16s_good3" (i64.const 0)) (i64.const 25442)) +(assert_return (invoke "16s_good4" (i64.const 0)) (i64.const 25699)) +(assert_return (invoke "16s_good5" (i64.const 0)) (i64.const 122)) + +(assert_return (invoke "32u_good1" (i64.const 0)) (i64.const 1684234849)) +(assert_return (invoke "32u_good2" (i64.const 0)) (i64.const 1684234849)) +(assert_return (invoke "32u_good3" (i64.const 0)) (i64.const 1701077858)) +(assert_return (invoke "32u_good4" (i64.const 0)) (i64.const 1717920867)) +(assert_return (invoke "32u_good5" (i64.const 0)) (i64.const 122)) + +(assert_return (invoke "32s_good1" (i64.const 0)) (i64.const 1684234849)) +(assert_return (invoke "32s_good2" (i64.const 0)) (i64.const 1684234849)) +(assert_return (invoke "32s_good3" (i64.const 0)) (i64.const 1701077858)) +(assert_return (invoke "32s_good4" (i64.const 0)) (i64.const 1717920867)) +(assert_return (invoke "32s_good5" (i64.const 0)) (i64.const 122)) + +(assert_return (invoke "64_good1" (i64.const 0)) (i64.const 0x6867666564636261)) +(assert_return (invoke "64_good2" (i64.const 0)) (i64.const 0x6867666564636261)) +(assert_return (invoke "64_good3" (i64.const 0)) (i64.const 0x6968676665646362)) +(assert_return (invoke "64_good4" (i64.const 0)) (i64.const 0x6a69686766656463)) +(assert_return (invoke "64_good5" (i64.const 0)) (i64.const 122)) + +(assert_return (invoke "8u_good1" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "8u_good2" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "8u_good3" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "8u_good4" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "8u_good5" (i64.const 65503)) (i64.const 0)) + +(assert_return (invoke "8s_good1" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "8s_good2" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "8s_good3" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "8s_good4" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "8s_good5" (i64.const 65503)) (i64.const 0)) + +(assert_return (invoke "16u_good1" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "16u_good2" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "16u_good3" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "16u_good4" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "16u_good5" (i64.const 65503)) (i64.const 0)) + +(assert_return (invoke "16s_good1" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "16s_good2" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "16s_good3" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "16s_good4" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "16s_good5" (i64.const 65503)) (i64.const 0)) + +(assert_return (invoke "32u_good1" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "32u_good2" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "32u_good3" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "32u_good4" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "32u_good5" (i64.const 65503)) (i64.const 0)) + +(assert_return (invoke "32s_good1" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "32s_good2" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "32s_good3" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "32s_good4" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "32s_good5" (i64.const 65503)) (i64.const 0)) + +(assert_return (invoke "64_good1" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "64_good2" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "64_good3" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "64_good4" (i64.const 65503)) (i64.const 0)) +(assert_return (invoke "64_good5" (i64.const 65503)) (i64.const 0)) + +(assert_return (invoke "8u_good1" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "8u_good2" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "8u_good3" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "8u_good4" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "8u_good5" (i64.const 65504)) (i64.const 0)) + +(assert_return (invoke "8s_good1" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "8s_good2" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "8s_good3" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "8s_good4" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "8s_good5" (i64.const 65504)) (i64.const 0)) + +(assert_return (invoke "16u_good1" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "16u_good2" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "16u_good3" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "16u_good4" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "16u_good5" (i64.const 65504)) (i64.const 0)) + +(assert_return (invoke "16s_good1" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "16s_good2" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "16s_good3" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "16s_good4" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "16s_good5" (i64.const 65504)) (i64.const 0)) + +(assert_return (invoke "32u_good1" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "32u_good2" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "32u_good3" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "32u_good4" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "32u_good5" (i64.const 65504)) (i64.const 0)) + +(assert_return (invoke "32s_good1" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "32s_good2" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "32s_good3" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "32s_good4" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "32s_good5" (i64.const 65504)) (i64.const 0)) + +(assert_return (invoke "64_good1" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "64_good2" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "64_good3" (i64.const 65504)) (i64.const 0)) +(assert_return (invoke "64_good4" (i64.const 65504)) (i64.const 0)) +(assert_trap (invoke "64_good5" (i64.const 65504)) "out of bounds memory access") + +(assert_trap (invoke "8u_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "8s_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "16u_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "16s_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "32u_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "32s_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "64_bad" (i64.const 0)) "out of bounds memory access") + +(assert_trap (invoke "8u_bad" (i64.const 1)) "out of bounds memory access") +(assert_trap (invoke "8s_bad" (i64.const 1)) "out of bounds memory access") +(assert_trap (invoke "16u_bad" (i64.const 1)) "out of bounds memory access") +(assert_trap (invoke "16s_bad" (i64.const 1)) "out of bounds memory access") +(assert_trap (invoke "32u_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "32s_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "64_bad" (i64.const 1)) "out of bounds memory access") + +;; Load f32 data with different offset/align arguments + +(module + (memory i64 1) + (data (i64.const 0) "\00\00\00\00\00\00\a0\7f\01\00\d0\7f") + + (func (export "32_good1") (param $i i64) (result f32) + (f32.load offset=0 (local.get $i)) ;; 0.0 '\00\00\00\00' + ) + (func (export "32_good2") (param $i i64) (result f32) + (f32.load align=1 (local.get $i)) ;; 0.0 '\00\00\00\00' + ) + (func (export "32_good3") (param $i i64) (result f32) + (f32.load offset=1 align=1 (local.get $i)) ;; 0.0 '\00\00\00\00' + ) + (func (export "32_good4") (param $i i64) (result f32) + (f32.load offset=2 align=2 (local.get $i)) ;; 0.0 '\00\00\00\00' + ) + (func (export "32_good5") (param $i i64) (result f32) + (f32.load offset=8 align=4 (local.get $i)) ;; nan:0x500001 '\01\00\d0\7f' + ) + (func (export "32_bad") (param $i i64) + (drop (f32.load offset=4294967295 (local.get $i))) + ) +) + +(assert_return (invoke "32_good1" (i64.const 0)) (f32.const 0.0)) +(assert_return (invoke "32_good2" (i64.const 0)) (f32.const 0.0)) +(assert_return (invoke "32_good3" (i64.const 0)) (f32.const 0.0)) +(assert_return (invoke "32_good4" (i64.const 0)) (f32.const 0.0)) +(assert_return (invoke "32_good5" (i64.const 0)) (f32.const nan:0x500001)) + +(assert_return (invoke "32_good1" (i64.const 65524)) (f32.const 0.0)) +(assert_return (invoke "32_good2" (i64.const 65524)) (f32.const 0.0)) +(assert_return (invoke "32_good3" (i64.const 65524)) (f32.const 0.0)) +(assert_return (invoke "32_good4" (i64.const 65524)) (f32.const 0.0)) +(assert_return (invoke "32_good5" (i64.const 65524)) (f32.const 0.0)) + +(assert_return (invoke "32_good1" (i64.const 65525)) (f32.const 0.0)) +(assert_return (invoke "32_good2" (i64.const 65525)) (f32.const 0.0)) +(assert_return (invoke "32_good3" (i64.const 65525)) (f32.const 0.0)) +(assert_return (invoke "32_good4" (i64.const 65525)) (f32.const 0.0)) +(assert_trap (invoke "32_good5" (i64.const 65525)) "out of bounds memory access") + +(assert_trap (invoke "32_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "32_bad" (i64.const 1)) "out of bounds memory access") + +;; Load f64 data with different offset/align arguments + +(module + (memory i64 1) + (data (i64.const 0) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\f4\7f\01\00\00\00\00\00\fc\7f") + + (func (export "64_good1") (param $i i64) (result f64) + (f64.load offset=0 (local.get $i)) ;; 0.0 '\00\00\00\00\00\00\00\00' + ) + (func (export "64_good2") (param $i i64) (result f64) + (f64.load align=1 (local.get $i)) ;; 0.0 '\00\00\00\00\00\00\00\00' + ) + (func (export "64_good3") (param $i i64) (result f64) + (f64.load offset=1 align=1 (local.get $i)) ;; 0.0 '\00\00\00\00\00\00\00\00' + ) + (func (export "64_good4") (param $i i64) (result f64) + (f64.load offset=2 align=2 (local.get $i)) ;; 0.0 '\00\00\00\00\00\00\00\00' + ) + (func (export "64_good5") (param $i i64) (result f64) + (f64.load offset=18 align=8 (local.get $i)) ;; nan:0xc000000000001 '\01\00\00\00\00\00\fc\7f' + ) + (func (export "64_bad") (param $i i64) + (drop (f64.load offset=4294967295 (local.get $i))) + ) +) + +(assert_return (invoke "64_good1" (i64.const 0)) (f64.const 0.0)) +(assert_return (invoke "64_good2" (i64.const 0)) (f64.const 0.0)) +(assert_return (invoke "64_good3" (i64.const 0)) (f64.const 0.0)) +(assert_return (invoke "64_good4" (i64.const 0)) (f64.const 0.0)) +(assert_return (invoke "64_good5" (i64.const 0)) (f64.const nan:0xc000000000001)) + +(assert_return (invoke "64_good1" (i64.const 65510)) (f64.const 0.0)) +(assert_return (invoke "64_good2" (i64.const 65510)) (f64.const 0.0)) +(assert_return (invoke "64_good3" (i64.const 65510)) (f64.const 0.0)) +(assert_return (invoke "64_good4" (i64.const 65510)) (f64.const 0.0)) +(assert_return (invoke "64_good5" (i64.const 65510)) (f64.const 0.0)) + +(assert_return (invoke "64_good1" (i64.const 65511)) (f64.const 0.0)) +(assert_return (invoke "64_good2" (i64.const 65511)) (f64.const 0.0)) +(assert_return (invoke "64_good3" (i64.const 65511)) (f64.const 0.0)) +(assert_return (invoke "64_good4" (i64.const 65511)) (f64.const 0.0)) +(assert_trap (invoke "64_good5" (i64.const 65511)) "out of bounds memory access") + +(assert_trap (invoke "64_bad" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "64_bad" (i64.const 1)) "out of bounds memory access") diff --git a/test/core/align.wast b/test/core/align.wast index 93064649d..91331f9a4 100644 --- a/test/core/align.wast +++ b/test/core/align.wast @@ -864,3 +864,101 @@ (assert_trap (invoke "store" (i32.const 65532) (i64.const -1)) "out of bounds memory access") ;; No memory was changed (assert_return (invoke "load" (i32.const 65532)) (i32.const 0)) + +;; Test invalid alignment values that may cause overflow when parsed. +;; These use the binary format, because it stores alignment as a base-2 exponent. + +;; Signed 32-bit overflow +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\05\03\01\00\01" ;; Memory section: 1 memory + "\0a\0a\01" ;; Code section: 1 function + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\28\1f\00" ;; i32.load offset=0 align=2**31 + "\1a" ;; drop + "\0b" ;; end + ) + "alignment must not be larger than natural" +) + +;; Unsigned 32-bit overflow +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\05\03\01\00\01" ;; Memory section: 1 memory + "\0a\0a\01" ;; Code section: 1 function + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\28\20\00" ;; i32.load offset=0 align=2**32 + "\1a" ;; drop + "\0b" ;; end + ) + "alignment must not be larger than natural" +) + +;; 32-bit out of range +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\05\03\01\00\01" ;; Memory section: 1 memory + "\0a\0a\01" ;; Code section: 1 function + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\28\21\00" ;; i32.load offset=0 align=2**33 + "\1a" ;; drop + "\0b" ;; end + ) + "alignment must not be larger than natural" +) + +;; Signed 64-bit overflow +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\05\03\01\00\01" ;; Memory section: 1 memory + "\0a\0a\01" ;; Code section: 1 function + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\28\3f\00" ;; i32.load offset=0 align=2**63 + "\1a" ;; drop + "\0b" ;; end + ) + "alignment must not be larger than natural" +) + +;; 64-bit out of range +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\05\03\01\00\01" ;; Memory section: 1 memory + "\0a\0a\01" ;; Code section: 1 function + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\28\41\00" ;; i32.load offset=0 align=2**65 (parsed as align=1, memidx present) + "\1a" ;; drop + "\0b" ;; end + ) + "type mismatch" +) diff --git a/test/core/align64.wast b/test/core/align64.wast new file mode 100644 index 000000000..da34947f6 --- /dev/null +++ b/test/core/align64.wast @@ -0,0 +1,866 @@ +;; Test alignment annotation rules + +(module (memory i64 0) (func (drop (i32.load8_s align=1 (i64.const 0))))) +(module (memory i64 0) (func (drop (i32.load8_u align=1 (i64.const 0))))) +(module (memory i64 0) (func (drop (i32.load16_s align=2 (i64.const 0))))) +(module (memory i64 0) (func (drop (i32.load16_u align=2 (i64.const 0))))) +(module (memory i64 0) (func (drop (i32.load align=4 (i64.const 0))))) +(module (memory i64 0) (func (drop (i64.load8_s align=1 (i64.const 0))))) +(module (memory i64 0) (func (drop (i64.load8_u align=1 (i64.const 0))))) +(module (memory i64 0) (func (drop (i64.load16_s align=2 (i64.const 0))))) +(module (memory i64 0) (func (drop (i64.load16_u align=2 (i64.const 0))))) +(module (memory i64 0) (func (drop (i64.load32_s align=4 (i64.const 0))))) +(module (memory i64 0) (func (drop (i64.load32_u align=4 (i64.const 0))))) +(module (memory i64 0) (func (drop (i64.load align=8 (i64.const 0))))) +(module (memory i64 0) (func (drop (f32.load align=4 (i64.const 0))))) +(module (memory i64 0) (func (drop (f64.load align=8 (i64.const 0))))) +(module (memory i64 0) (func (i32.store8 align=1 (i64.const 0) (i32.const 1)))) +(module (memory i64 0) (func (i32.store16 align=2 (i64.const 0) (i32.const 1)))) +(module (memory i64 0) (func (i32.store align=4 (i64.const 0) (i32.const 1)))) +(module (memory i64 0) (func (i64.store8 align=1 (i64.const 0) (i64.const 1)))) +(module (memory i64 0) (func (i64.store16 align=2 (i64.const 0) (i64.const 1)))) +(module (memory i64 0) (func (i64.store32 align=4 (i64.const 0) (i64.const 1)))) +(module (memory i64 0) (func (i64.store align=8 (i64.const 0) (i64.const 1)))) +(module (memory i64 0) (func (f32.store align=4 (i64.const 0) (f32.const 1.0)))) +(module (memory i64 0) (func (f64.store align=8 (i64.const 0) (f64.const 1.0)))) + +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i32.load8_s align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i32.load8_s align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i32.load8_u align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i32.load8_u align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i32.load16_s align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i32.load16_s align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i32.load16_u align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i32.load16_u align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i32.load align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i32.load align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load8_s align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load8_s align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load8_u align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load8_u align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load16_s align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load16_s align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load16_u align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load16_u align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load32_s align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load32_s align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load32_u align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load32_u align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (i64.load align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (f32.load align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (f32.load align=7 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (f64.load align=0 (i64.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (drop (f64.load align=7 (i64.const 0)))))" + ) + "alignment" +) + +(assert_malformed + (module quote + "(module (memory i64 0) (func (i32.store8 align=0 (i64.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i32.store8 align=7 (i64.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i32.store16 align=0 (i64.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i32.store16 align=7 (i64.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i32.store align=0 (i64.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i32.store align=7 (i64.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i64.store8 align=0 (i64.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i64.store8 align=7 (i64.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i64.store16 align=0 (i64.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i64.store16 align=7 (i64.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i64.store32 align=0 (i64.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i64.store32 align=7 (i64.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i64.store align=0 (i64.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (i64.store align=7 (i64.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (f32.store align=0 (i64.const 0) (f32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (f32.store align=7 (i64.const 0) (f32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (f64.store align=0 (i64.const 0) (f32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory i64 0) (func (f64.store align=7 (i64.const 0) (f32.const 0))))" + ) + "alignment" +) + +(assert_invalid + (module (memory i64 0) (func (drop (i32.load8_s align=2 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i32.load8_u align=2 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i32.load16_s align=4 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i32.load16_u align=4 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i32.load align=8 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load8_s align=2 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load8_u align=2 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load16_s align=4 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load16_u align=4 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load32_s align=8 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load32_u align=8 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load align=16 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (f32.load align=8 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (f64.load align=16 (i64.const 0))))) + "alignment must not be larger than natural" +) + +(assert_invalid + (module (memory i64 0) (func (drop (i32.load8_s align=2 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i32.load8_u align=2 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i32.load16_s align=4 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i32.load16_u align=4 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i32.load align=8 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load8_s align=2 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load8_u align=2 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load16_s align=4 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load16_u align=4 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load32_s align=8 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load32_u align=8 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (i64.load align=16 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (f32.load align=8 (i64.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (drop (f64.load align=16 (i64.const 0))))) + "alignment must not be larger than natural" +) + +(assert_invalid + (module (memory i64 0) (func (i32.store8 align=2 (i64.const 0) (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (i32.store16 align=4 (i64.const 0) (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (i32.store align=8 (i64.const 0) (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (i64.store8 align=2 (i64.const 0) (i64.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (i64.store16 align=4 (i64.const 0) (i64.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (i64.store32 align=8 (i64.const 0) (i64.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (i64.store align=16 (i64.const 0) (i64.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (f32.store align=8 (i64.const 0) (f32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory i64 0) (func (f64.store align=16 (i64.const 0) (f64.const 0)))) + "alignment must not be larger than natural" +) + +;; Test aligned and unaligned read/write + +(module + (memory i64 1) + + ;; $default: natural alignment, $1: align=1, $2: align=2, $4: align=4, $8: align=8 + + (func (export "f32_align_switch") (param i32) (result f32) + (local f32 f32) + (local.set 1 (f32.const 10.0)) + (block $4 + (block $2 + (block $1 + (block $default + (block $0 + (br_table $0 $default $1 $2 $4 (local.get 0)) + ) ;; 0 + (f32.store (i64.const 0) (local.get 1)) + (local.set 2 (f32.load (i64.const 0))) + (br $4) + ) ;; default + (f32.store align=1 (i64.const 0) (local.get 1)) + (local.set 2 (f32.load align=1 (i64.const 0))) + (br $4) + ) ;; 1 + (f32.store align=2 (i64.const 0) (local.get 1)) + (local.set 2 (f32.load align=2 (i64.const 0))) + (br $4) + ) ;; 2 + (f32.store align=4 (i64.const 0) (local.get 1)) + (local.set 2 (f32.load align=4 (i64.const 0))) + ) ;; 4 + (local.get 2) + ) + + (func (export "f64_align_switch") (param i32) (result f64) + (local f64 f64) + (local.set 1 (f64.const 10.0)) + (block $8 + (block $4 + (block $2 + (block $1 + (block $default + (block $0 + (br_table $0 $default $1 $2 $4 $8 (local.get 0)) + ) ;; 0 + (f64.store (i64.const 0) (local.get 1)) + (local.set 2 (f64.load (i64.const 0))) + (br $8) + ) ;; default + (f64.store align=1 (i64.const 0) (local.get 1)) + (local.set 2 (f64.load align=1 (i64.const 0))) + (br $8) + ) ;; 1 + (f64.store align=2 (i64.const 0) (local.get 1)) + (local.set 2 (f64.load align=2 (i64.const 0))) + (br $8) + ) ;; 2 + (f64.store align=4 (i64.const 0) (local.get 1)) + (local.set 2 (f64.load align=4 (i64.const 0))) + (br $8) + ) ;; 4 + (f64.store align=8 (i64.const 0) (local.get 1)) + (local.set 2 (f64.load align=8 (i64.const 0))) + ) ;; 8 + (local.get 2) + ) + + ;; $8s: i32/i64.load8_s, $8u: i32/i64.load8_u, $16s: i32/i64.load16_s, $16u: i32/i64.load16_u, $32: i32.load + ;; $32s: i64.load32_s, $32u: i64.load32_u, $64: i64.load + + (func (export "i32_align_switch") (param i32 i32) (result i32) + (local i32 i32) + (local.set 2 (i32.const 10)) + (block $32 + (block $16u + (block $16s + (block $8u + (block $8s + (block $0 + (br_table $0 $8s $8u $16s $16u $32 (local.get 0)) + ) ;; 0 + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i32.store8 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load8_s (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i32.store8 align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load8_s align=1 (i64.const 0))) + ) + ) + (br $32) + ) ;; 8s + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i32.store8 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load8_u (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i32.store8 align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load8_u align=1 (i64.const 0))) + ) + ) + (br $32) + ) ;; 8u + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i32.store16 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load16_s (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i32.store16 align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load16_s align=1 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i32.store16 align=2 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load16_s align=2 (i64.const 0))) + ) + ) + (br $32) + ) ;; 16s + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i32.store16 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load16_u (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i32.store16 align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load16_u align=1 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i32.store16 align=2 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load16_u align=2 (i64.const 0))) + ) + ) + (br $32) + ) ;; 16u + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i32.store (i64.const 0) (local.get 2)) + (local.set 3 (i32.load (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i32.store align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load align=1 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i32.store align=2 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load align=2 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 4)) + (then + (i32.store align=4 (i64.const 0) (local.get 2)) + (local.set 3 (i32.load align=4 (i64.const 0))) + ) + ) + ) ;; 32 + (local.get 3) + ) + + (func (export "i64_align_switch") (param i32 i32) (result i64) + (local i64 i64) + (local.set 2 (i64.const 10)) + (block $64 + (block $32u + (block $32s + (block $16u + (block $16s + (block $8u + (block $8s + (block $0 + (br_table $0 $8s $8u $16s $16u $32s $32u $64 (local.get 0)) + ) ;; 0 + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store8 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load8_s (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store8 align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load8_s align=1 (i64.const 0))) + ) + ) + (br $64) + ) ;; 8s + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store8 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load8_u (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store8 align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load8_u align=1 (i64.const 0))) + ) + ) + (br $64) + ) ;; 8u + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store16 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load16_s (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store16 align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load16_s align=1 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i64.store16 align=2 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load16_s align=2 (i64.const 0))) + ) + ) + (br $64) + ) ;; 16s + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store16 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load16_u (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store16 align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load16_u align=1 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i64.store16 align=2 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load16_u align=2 (i64.const 0))) + ) + ) + (br $64) + ) ;; 16u + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store32 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load32_s (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store32 align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load32_s align=1 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i64.store32 align=2 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load32_s align=2 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 4)) + (then + (i64.store32 align=4 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load32_s align=4 (i64.const 0))) + ) + ) + (br $64) + ) ;; 32s + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store32 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load32_u (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store32 align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load32_u align=1 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i64.store32 align=2 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load32_u align=2 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 4)) + (then + (i64.store32 align=4 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load32_u align=4 (i64.const 0))) + ) + ) + (br $64) + ) ;; 32u + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store (i64.const 0) (local.get 2)) + (local.set 3 (i64.load (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store align=1 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load align=1 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i64.store align=2 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load align=2 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 4)) + (then + (i64.store align=4 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load align=4 (i64.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 8)) + (then + (i64.store align=8 (i64.const 0) (local.get 2)) + (local.set 3 (i64.load align=8 (i64.const 0))) + ) + ) + ) ;; 64 + (local.get 3) + ) +) + +(assert_return (invoke "f32_align_switch" (i32.const 0)) (f32.const 10.0)) +(assert_return (invoke "f32_align_switch" (i32.const 1)) (f32.const 10.0)) +(assert_return (invoke "f32_align_switch" (i32.const 2)) (f32.const 10.0)) +(assert_return (invoke "f32_align_switch" (i32.const 3)) (f32.const 10.0)) + +(assert_return (invoke "f64_align_switch" (i32.const 0)) (f64.const 10.0)) +(assert_return (invoke "f64_align_switch" (i32.const 1)) (f64.const 10.0)) +(assert_return (invoke "f64_align_switch" (i32.const 2)) (f64.const 10.0)) +(assert_return (invoke "f64_align_switch" (i32.const 3)) (f64.const 10.0)) +(assert_return (invoke "f64_align_switch" (i32.const 4)) (f64.const 10.0)) + +(assert_return (invoke "i32_align_switch" (i32.const 0) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 0) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 1) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 1) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 2) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 2) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 2) (i32.const 2)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 3) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 3) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 3) (i32.const 2)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 4) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 4) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 4) (i32.const 2)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 4) (i32.const 4)) (i32.const 10)) + +(assert_return (invoke "i64_align_switch" (i32.const 0) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 0) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 1) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 1) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 2) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 2) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 2) (i32.const 2)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 3) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 3) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 3) (i32.const 2)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 4) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 4) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 4) (i32.const 2)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 4) (i32.const 4)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 5) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 5) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 5) (i32.const 2)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 5) (i32.const 4)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 6) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 6) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 6) (i32.const 2)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 6) (i32.const 4)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 6) (i32.const 8)) (i64.const 10)) + +;; Test that an i64 store with 4-byte alignment that's 4 bytes out of bounds traps without storing anything + +(module + (memory i64 1) + (func (export "store") (param i64 i64) + (i64.store align=4 (local.get 0) (local.get 1)) + ) + (func (export "load") (param i64) (result i32) + (i32.load (local.get 0)) + ) +) + +(assert_trap (invoke "store" (i64.const 65532) (i64.const -1)) "out of bounds memory access") +;; No memory was changed +(assert_return (invoke "load" (i64.const 65532)) (i32.const 0)) diff --git a/test/core/annotations.wast b/test/core/annotations.wast new file mode 100644 index 000000000..912d10f38 --- /dev/null +++ b/test/core/annotations.wast @@ -0,0 +1,207 @@ +(module + (@a) + (@0) + (@aas-3!@$d-@#4) + (@@) (@$) (@+) (@0) (@.) (@!$@#$23414@#$) + (@"a") + (@" @ asd\2a 045 \" fdaf \t \u{45}") + + (@a x y z) + (@a x-y $yz "aa" -2 0.3 0x3) + (@a x-y$yz"aa"-2) + (@a block func module i32.add) + (@a 0x 8q 0xfa #4g0-.@f#^&@#$*0sf -- @#) + (@a , ; ] [ }} }x{ ({) ,{{};}] ;) + (@a (bla) () (5-g) ("aa" a) ($x) (bla bla) (x (y)) ")" "(" x")"y) + (@a @ @x (@x) (@x y) (@) (@ x) (@(@(@(@))))) + (@a (;bla;) (; ) ;) + ;; bla) + ;; bla (@x + ) +) + +(assert_malformed (module quote "(@a \00)") "illegal character") +(assert_malformed (module quote "(@a \01)") "illegal character") +(assert_malformed (module quote "(@a \02)") "illegal character") +(assert_malformed (module quote "(@a \03)") "illegal character") +(assert_malformed (module quote "(@a \04)") "illegal character") +(assert_malformed (module quote "(@a \05)") "illegal character") +(assert_malformed (module quote "(@a \06)") "illegal character") +(assert_malformed (module quote "(@a \07)") "illegal character") +(assert_malformed (module quote "(@a \08)") "illegal character") +(module quote "(@a \09)") ;; \t +(module quote "(@a \0a)") ;; \n +(assert_malformed (module quote "(@a \0b)") "illegal character") +(assert_malformed (module quote "(@a \0c)") "illegal character") +(module quote "(@a \0d)") ;; \r +(assert_malformed (module quote "(@a \0e)") "illegal character") +(assert_malformed (module quote "(@a \0f)") "illegal character") +(assert_malformed (module quote "(@a \10)") "illegal character") +(assert_malformed (module quote "(@a \11)") "illegal character") +(assert_malformed (module quote "(@a \12)") "illegal character") +(assert_malformed (module quote "(@a \13)") "illegal character") +(assert_malformed (module quote "(@a \14)") "illegal character") +(assert_malformed (module quote "(@a \15)") "illegal character") +(assert_malformed (module quote "(@a \16)") "illegal character") +(assert_malformed (module quote "(@a \17)") "illegal character") +(assert_malformed (module quote "(@a \18)") "illegal character") +(assert_malformed (module quote "(@a \19)") "illegal character") +(assert_malformed (module quote "(@a \1a)") "illegal character") +(assert_malformed (module quote "(@a \1b)") "illegal character") +(assert_malformed (module quote "(@a \1c)") "illegal character") +(assert_malformed (module quote "(@a \1d)") "illegal character") +(assert_malformed (module quote "(@a \1e)") "illegal character") +(assert_malformed (module quote "(@a \1f)") "illegal character") +(module quote "(@a \20)") ;; space +(assert_malformed (module quote "(@a \7f)") "illegal character") +(assert_malformed (module quote "(@a \80)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \81)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \90)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \a0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \b0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \c0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \d0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \e0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \f0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \ff)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a Heiße Würstchen)") "illegal character") +(assert_malformed (module quote "(@a )") "illegal character") + +(assert_malformed (module quote "( @a)") "unknown operator") + +(assert_malformed (module quote "(@)") "empty annotation id") +(assert_malformed (module quote "(@ )") "empty annotation id") +(assert_malformed (module quote "(@ x)") "empty annotation id") +(assert_malformed (module quote "(@(@a)x)") "empty annotation id") +(assert_malformed (module quote "(@\"\")") "empty annotation id") +(assert_malformed (module quote "(@ \"a\")") "empty annotation id") +(assert_malformed (module quote "(@\"\n\")") "empty annotation id") +(assert_malformed (module quote "(@\"\\ef\")") "malformed UTF-8") + +(assert_malformed (module quote "(@x ") "unclosed annotation") +(assert_malformed (module quote "(@x ()") "unclosed annotation") +(assert_malformed (module quote "(@x (y (z))") "unclosed annotation") +(assert_malformed (module quote "(@x (@y )") "unclosed annotation") + +(assert_malformed (module quote "(@x))") "unexpected token") +(assert_malformed (module quote "(@x ()))") "unexpected token") +(assert_malformed (module quote "(@x (y (z))))") "unexpected token") +(assert_malformed (module quote "(@x (@y )))") "unexpected token") + +(assert_malformed (module quote "(@x \"") "unclosed string") +(assert_malformed (module quote "(@x \")") "unclosed string") + +(assert_malformed (module quote "((@a)@b)") "unknown operator") +(assert_malformed (module quote "(func $(@a))") "empty identifier") +(assert_malformed (module quote "(func $(@a)f)") "empty identifier") + +((@a) module (@a) $m (@a) (@a) + ((@a) import (@a) "spectest" (@a) "global_i32" (@a) + ((@a) global (@a) $g (@a) i32 (@a)) (@a) + ) (@a) + ((@a) import (@a) "spectest" (@a) "table" (@a) + ((@a) table (@a) $t (@a) 10 (@a) 20 (@a) funcref (@a)) (@a) + ) (@a) + ((@a) import (@a) "spectest" (@a) "memory" (@a) + ((@a) memory (@a) $m (@a) 1 (@a) 2 (@a)) (@a) + ) (@a) + ((@a) import (@a) "spectest" (@a) "print_i32_f32" (@a) + ((@a) func (@a) $f (@a) + ((@a) param (@a) i32 (@a) f32 (@a)) (@a) + ((@a) result (@a)) (@a) + ) (@a) + ) (@a) + + ((@a) export (@a) "g" (@a) + ((@a) global (@a) $g (@a)) (@a) + ) (@a) + ((@a) export (@a) "t" (@a) + ((@a) table (@a) $t (@a)) (@a) + ) (@a) + ((@a) export (@a) "m" (@a) + ((@a) memory (@a) $m (@a)) (@a) + ) (@a) + ((@a) export (@a) "f" (@a) + ((@a) func (@a) $f (@a)) (@a) + ) (@a) +) + +((@a) module (@a) $m1 (@a) (@a) + ((@a) global (@a) $g (@a) + ((@a) export (@a) "g" (@a)) (@a) + ((@a) import (@a) "spectest" (@a) "global_i32" (@a)) (@a) + i32 (@a) + ) (@a) + ((@a) table (@a) $t (@a) + ((@a) export (@a) "t" (@a)) (@a) + ((@a) import (@a) "spectest" (@a) "table" (@a)) (@a) + 10 (@a) 20 (@a) + funcref (@a) + ) (@a) + ((@a) memory (@a) $m (@a) + ((@a) export (@a) "m" (@a)) (@a) + ((@a) import (@a) "spectest" (@a) "memory" (@a)) (@a) + 1 (@a) 2 (@a) + ) (@a) + ((@a) func (@a) $f (@a) + ((@a) export (@a) "f" (@a)) (@a) + ((@a) import (@a) "spectest" (@a) "print_i32_f32" (@a)) (@a) + ((@a) param (@a) i32 (@a) f32 (@a)) (@a) + ((@a) result (@a)) (@a) + ) (@a) +) + +((@a) module (@a) $m2 (@a) (@a) + ((@a) type (@a) $T (@a) + ((@a) func (@a) + ((@a) param (@a) i32 (@a) i64 (@a)) (@a) + ((@a) param (@a) $x (@a) i32 (@a)) (@a) + ((@a) result (@a) i32 (@a)) (@a) + ) (@a) + ) (@a) + + ((@a) global (@a) $g (@a) + ((@a) export (@a) "g" (@a)) (@a) + i32 (@a) + ((@a) i32.const (@a) 42 (@a)) (@a) + ) (@a) + ((@a) table (@a) $t (@a) + ((@a) export (@a) "t" (@a)) (@a) + 10 (@a) 20 (@a) + funcref (@a) + ) (@a) + ((@a) memory (@a) $m (@a) + ((@a) export (@a) "m" (@a)) (@a) + 1 (@a) 2 (@a) + ) (@a) + ((@a) func (@a) $f (@a) + ((@a) export (@a) "f" (@a)) (@a) + ((@a) param (@a) i32 (@a) i64 (@a)) (@a) + ((@a) param (@a) $x (@a) i32 (@a)) (@a) + ((@a) result (@a) i32 (@a)) (@a) + ((@a) local (@a) i32 (@a) i32 (@a)) (@a) + ((@a) local (@a) $y (@a) i32 (@a)) (@a) + ((@a) block (@a) + ((@a) result (@a) i32 (@a)) (@a) + ((@a) i32.add (@a) + ((@a) local.get (@a) $x (@a)) (@a) + ((@a) local.get (@a) 0 (@a)) (@a) + ) + ) + ) (@a) + + ((@a) elem (@a) + ((@a) offset (@a) ((@a) i32.const (@a) 0 (@a)) (@a)) (@a) + $f (@a) $f (@a) (@a) $f (@a) + ) (@a) + ((@a) data (@a) + ((@a) offset (@a) ((@a) i32.const (@a) 0 (@a)) (@a)) (@a) + "bla" (@a) "\43" (@a) (@a) "" (@a) + ) (@a) + + (func $s) + ((@a) start (@a) $s (@a)) (@a) +) + +(module quote "(@a) (func)") +(module quote "(func) (@a)") diff --git a/test/core/binary-leb128.wast b/test/core/binary-leb128.wast index 335496f08..c6b79eb95 100644 --- a/test/core/binary-leb128.wast +++ b/test/core/binary-leb128.wast @@ -217,8 +217,8 @@ (assert_malformed (module binary "\00asm" "\01\00\00\00" - "\05\08\01" ;; Memory section with 1 entry - "\00\82\80\80\80\80\00" ;; no max, minimum 2 with one byte too many + "\05\08\01" ;; Memory section with 1 entry + "\00\82\80\80\80\80\80\80\80\80\80\00" ;; no max, minimum 2 with one byte too many ) "integer representation too long" ) @@ -227,7 +227,7 @@ "\00asm" "\01\00\00\00" "\05\0a\01" ;; Memory section with 1 entry "\01\82\00" ;; minimum 2 - "\82\80\80\80\80\00" ;; max 2 with one byte too many + "\82\80\80\80\80\80\80\80\80\80\00" ;; max 2 with one byte too many ) "integer representation too long" ) @@ -526,7 +526,7 @@ (module binary "\00asm" "\01\00\00\00" "\05\07\01" ;; Memory section with 1 entry - "\00\82\80\80\80\70" ;; no max, minimum 2 with unused bits set + "\00\82\80\80\80\80\80\80\80\80\70" ;; no max, minimum 2 with unused bits set ) "integer too large" ) @@ -534,7 +534,7 @@ (module binary "\00asm" "\01\00\00\00" "\05\07\01" ;; Memory section with 1 entry - "\00\82\80\80\80\40" ;; no max, minimum 2 with some unused bits set + "\00\82\80\80\80\80\80\80\80\80\40" ;; no max, minimum 2 with some unused bits set ) "integer too large" ) @@ -543,7 +543,7 @@ "\00asm" "\01\00\00\00" "\05\09\01" ;; Memory section with 1 entry "\01\82\00" ;; minimum 2 - "\82\80\80\80\10" ;; max 2 with unused bits set + "\82\80\80\80\80\80\80\80\80\10" ;; max 2 with unused bits set ) "integer too large" ) @@ -552,7 +552,7 @@ "\00asm" "\01\00\00\00" "\05\09\01" ;; Memory section with 1 entry "\01\82\00" ;; minimum 2 - "\82\80\80\80\40" ;; max 2 with some unused bits set + "\82\80\80\80\80\80\80\80\80\40" ;; max 2 with some unused bits set ) "integer too large" ) @@ -744,8 +744,7 @@ "\1a" ;; drop "\0b" ;; end ) - ;; TODO: This changes to "integer too large" with memory64. - "integer representation too long" + "integer too large" ) (assert_malformed (module binary @@ -764,8 +763,7 @@ "\1a" ;; drop "\0b" ;; end ) - ;; TODO: This changes to "integer too large" with memory64. - "integer representation too long" + "integer too large" ) (assert_malformed (module binary @@ -859,8 +857,7 @@ "\82\80\80\80\80\80\80\80\80\10" ;; offset 2 with unused bits set "\0b" ;; end ) - ;; TODO: This changes to "integer too large" with memory64. - "integer representation too long" + "integer too large" ) (assert_malformed (module binary @@ -879,8 +876,40 @@ "\82\80\80\80\80\80\80\80\80\40" ;; offset 2 with some unused bits set "\0b" ;; end ) - ;; TODO: This changes to "integer too large" with memory64. - "integer representation too long" + "integer too large" +) +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\04\00" ;; Memory section (flags: i64) + "\0a\13\01" ;; Code section + ;; function 0 + "\11\00" ;; local type count + "\42\00" ;; i64.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" ;; offset 2^64 - 1 + "\1a" ;; drop + "\0b" ;; end +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\04\00" ;; Memory section (flags: i64) + "\0a\13\01" ;; Code section + ;; function 0 + "\11\00" ;; local type count + "\42\00" ;; i64.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\ff\ff\ff\ff\ff\ff\ff\ff\ff\02" ;; offset 2^64 (one unused bit set) + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" ) ;; Signed LEB128s sign-extend diff --git a/test/core/binary.wast b/test/core/binary.wast index 9a5122cdb..38813935d 100644 --- a/test/core/binary.wast +++ b/test/core/binary.wast @@ -258,27 +258,47 @@ "\0a\01\00" ;; Code section with 0 functions ) -;; Fewer passive segments than datacount +;; Fewer passive segments than data count (assert_malformed (module binary "\00asm" "\01\00\00\00" - "\0c\01\03" ;; Datacount section with value "3" - "\0b\05\02" ;; Data section with two entries - "\01\00" ;; Passive data section - "\01\00") ;; Passive data section - "data count and data section have inconsistent lengths") + "\0c\01\03" ;; Data count section with value 3 + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00" ;; Passive data section + ) + "data count and data section have inconsistent lengths" +) + +;; More passive segments than data count +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0c\01\01" ;; Data count section with value 1 + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00" ;; Passive data section + ) + "data count and data section have inconsistent lengths" +) -;; More passive segments than datacount +;; Non-zero data count section without data section (assert_malformed (module binary "\00asm" "\01\00\00\00" - "\0c\01\01" ;; Datacount section with value "1" - "\0b\05\02" ;; Data section with two entries - "\01\00" ;; Passive data section - "\01\00") ;; Passive data section - "data count and data section have inconsistent lengths") + "\05\03\01\00\01" ;; Memory section with one entry + "\0c\01\01" ;; Data count section with value 1 + ) + "data count and data section have inconsistent lengths" +) + +;; Zero data count section without data section +(module binary + "\00asm" "\01\00\00\00" + "\0c\01\00" ;; Data count section with value 0 +) -;; memory.init requires a datacount section +;; memory.init requires a data count section (assert_malformed (module binary "\00asm" "\01\00\00\00" @@ -298,9 +318,10 @@ "\0b\03\01\01\00" ;; Data section ) ;; end - "data count section required") + "data count section required" +) -;; data.drop requires a datacount section +;; data.drop requires a data count section (assert_malformed (module binary "\00asm" "\01\00\00\00" @@ -317,7 +338,8 @@ "\0b\03\01\01\00" ;; Data section ) ;; end - "data count section required") + "data count section required" +) ;; passive element segment containing illegal opcode (assert_malformed @@ -342,8 +364,10 @@ ;; function 0 "\02\00" - "\0b") ;; end - "illegal opcode") + "\0b" ;; end + ) + "illegal opcode" +) ;; passive element segment containing type other than funcref (assert_malformed @@ -368,8 +392,10 @@ ;; function 0 "\02\00" - "\0b") ;; end - "malformed reference type") + "\0b" ;; end + ) + "malformed reference type" +) ;; passive element segment containing opcode ref.func (module binary @@ -393,7 +419,8 @@ ;; function 0 "\02\00" - "\0b") ;; end + "\0b" ;; end +) ;; passive element segment containing opcode ref.null (module binary @@ -417,7 +444,8 @@ ;; function 0 "\02\00" - "\0b") ;; end + "\0b" ;; end +) ;; Type count can be zero @@ -587,19 +615,19 @@ "\00asm" "\01\00\00\00" "\04\03\01" ;; table section with one entry "\70" ;; anyfunc - "\02" ;; malformed table limits flag + "\08" ;; malformed table limits flag ) - "integer too large" + "malformed limits flags" ) (assert_malformed (module binary "\00asm" "\01\00\00\00" "\04\04\01" ;; table section with one entry "\70" ;; anyfunc - "\02" ;; malformed table limits flag + "\08" ;; malformed table limits flag "\00" ;; dummy byte ) - "integer too large" + "malformed limits flags" ) (assert_malformed (module binary @@ -609,7 +637,7 @@ "\81\00" ;; malformed table limits flag as LEB128 "\00\00" ;; dummy bytes ) - "integer representation too long" + "malformed limits flags" ) ;; Memory count can be zero @@ -633,18 +661,18 @@ (module binary "\00asm" "\01\00\00\00" "\05\02\01" ;; memory section with one entry - "\02" ;; malformed memory limits flag + "\08" ;; malformed memory limits flag ) - "integer too large" + "malformed limits flags" ) (assert_malformed (module binary "\00asm" "\01\00\00\00" "\05\03\01" ;; memory section with one entry - "\02" ;; malformed memory limits flag + "\08" ;; malformed memory limits flag "\00" ;; dummy byte ) - "integer too large" + "malformed limits flags" ) (assert_malformed (module binary @@ -653,7 +681,7 @@ "\81\00" ;; malformed memory limits flag as LEB128 "\00\00" ;; dummy bytes ) - "integer representation too long" + "malformed limits flags" ) (assert_malformed (module binary @@ -662,7 +690,7 @@ "\81\01" ;; malformed memory limits flag as LEB128 "\00\00" ;; dummy bytes ) - "integer representation too long" + "malformed limits flags" ) ;; Global count can be zero @@ -953,3 +981,230 @@ ) "unexpected content after last section" ) + +;; Multiple function sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section with 1 function + "\03\02\01\00" ;; Function section with 1 function + "\0a\07\02\02\00\0b\02\00\0b" ;; Code section with 2 empty functions + ) + "unexpected content after last section" +) + +;; Multiple code sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\03\02\00\00" ;; Function section with 2 functions + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + ) + "unexpected content after last section" +) + +;; Multiple data count sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0c\01\01" ;; Data count section with value "1" + "\0c\01\01" ;; Data count section with value "1" + ) + "unexpected content after last section" +) + +;; Multiple data sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0b\01\00" ;; Data section with zero entries + "\0b\01\00" ;; Data section with zero entries + ) + "unexpected content after last section" +) + +;; Multiple global sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\01\00" ;; Global section with zero entries + "\06\01\00" ;; Global section with zero entries + ) + "unexpected content after last section" +) + +;; Multiple export sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\07\01\00" ;; Export section with zero entries + "\07\01\00" ;; Export section with zero entries + ) + "unexpected content after last section" +) + +;; Multiple table sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\04\01\00" ;; Table section with zero entries + "\04\01\00" ;; Table section with zero entries + ) + "unexpected content after last section" +) + +;; Multiple element sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\09\01\00" ;; Element section with zero entries + "\09\01\00" ;; Element section with zero entries + ) + "unexpected content after last section" +) + +;; Multiple import sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\01\00" ;; Import section with zero entries + "\02\01\00" ;; Import section with zero entries + ) + "unexpected content after last section" +) + +;; Multiple type sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\01\00" ;; Type section with zero entries + "\01\01\00" ;; Type section with zero entries + ) + "unexpected content after last section" +) + +;; Multiple memory sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\01\00" ;; Memory section with zero entries + "\05\01\00" ;; Memory section with zero entries + ) + "unexpected content after last section" +) + +;; Type section out of order +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\01\00" ;; Import section with zero entries + "\01\01\00" ;; Type section with zero entries + ) + "unexpected content after last section" +) + +;; Import section out of order +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\03\01\00" ;; Function section with zero entries + "\02\01\00" ;; Import section with zero entries + ) + "unexpected content after last section" +) + +;; Function section out of order +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\04\01\00" ;; Table section with zero entries + "\03\01\00" ;; Function section with zero entries + ) + "unexpected content after last section" +) + +;; Table section out of order +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\01\00" ;; Memory section with zero entries + "\04\01\00" ;; Table section with zero entries + ) + "unexpected content after last section" +) + +;; Memory section out of order +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\01\00" ;; Global section with zero entries + "\05\01\00" ;; Memory section with zero entries + ) + "unexpected content after last section" +) + +;; Global section out of order +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\07\01\00" ;; Export section with zero entries + "\06\01\00" ;; Global section with zero entries + ) + "unexpected content after last section" +) + +;; Export section out of order +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\03\02\01\00" ;; Function section + "\08\01\00" ;; Start section: function 0 + "\07\01\00" ;; Export section with zero entries + ) + "unexpected content after last section" +) + +;; Start section out of order +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\03\02\01\00" ;; Function section + "\09\01\00" ;; Element section with zero entries + "\08\01\00" ;; Start section: function 0 + ) + "unexpected content after last section" +) + +;; Element section out of order +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0c\01\01" ;; Data count section with value "1" + "\09\01\00" ;; Element section with zero entries + ) + "unexpected content after last section" +) + +;; Data count section out of order +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0a\01\00" ;; Code section with zero entries + "\0c\01\01" ;; Data count section with value "1" + ) + "unexpected content after last section" +) + +;; Code section out of order +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0b\01\00" ;; Data section with zero entries + "\0a\01\00" ;; Code section with zero entries + ) + "unexpected content after last section" +) + diff --git a/test/core/bulk64._wast b/test/core/bulk64._wast new file mode 100644 index 000000000..9f82f1667 --- /dev/null +++ b/test/core/bulk64._wast @@ -0,0 +1,179 @@ +;; segment syntax +(module + (memory i64 1) + (data "foo")) + +;; memory.fill +(module + (memory i64 1) + + (func (export "fill") (param i64 i32 i64) + (memory.fill + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0))) +) + +;; Basic fill test. +(invoke "fill" (i64.const 1) (i32.const 0xff) (i64.const 3)) +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 0)) + +;; Fill value is stored as a byte. +(invoke "fill" (i64.const 0) (i32.const 0xbbaa) (i64.const 2)) +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0xaa)) + +;; Fill all of memory +(invoke "fill" (i64.const 0) (i32.const 0) (i64.const 0x10000)) + +;; Succeed when writing 0 bytes at the end of the region. +(invoke "fill" (i64.const 0x10000) (i32.const 0) (i64.const 0)) + +;; Writing 0 bytes outside of memory limit is NOT allowed. +(assert_trap + (invoke "fill" (i64.const 0x10001) (i32.const 0) (i64.const 0)) + "out of bounds") + +;; memory.copy +(module + (memory i64 1 1) + (data (i64.const 0) "\aa\bb\cc\dd") + + (func (export "copy") (param i64 i64 i64) + (memory.copy + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0))) +) + +;; Non-overlapping copy. +(invoke "copy" (i64.const 10) (i64.const 0) (i64.const 4)) + +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 0)) + +;; Overlap, source > dest +(invoke "copy" (i64.const 8) (i64.const 10) (i64.const 4)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 0xdd)) + +;; Overlap, source < dest +(invoke "copy" (i64.const 10) (i64.const 7) (i64.const 6)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 0)) + +;; Overlap, source < dest but size is out of bounds +(assert_trap + (invoke "copy" (i64.const 13) (i64.const 11) (i64.const -1)) + "out of bounds") +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 0)) + +;; Copy ending at memory limit is ok. +(invoke "copy" (i64.const 0xff00) (i64.const 0) (i64.const 0x100)) +(invoke "copy" (i64.const 0xfe00) (i64.const 0xff00) (i64.const 0x100)) + +;; Succeed when copying 0 bytes at the end of the region. +(invoke "copy" (i64.const 0x10000) (i64.const 0) (i64.const 0)) +(invoke "copy" (i64.const 0) (i64.const 0x10000) (i64.const 0)) + +;; Copying 0 bytes outside of memory limit is NOT allowed. +(assert_trap + (invoke "copy" (i64.const 0x10001) (i64.const 0) (i64.const 0)) + "out of bounds") +(assert_trap + (invoke "copy" (i64.const 0) (i64.const 0x10001) (i64.const 0)) + "out of bounds") + +;; memory.init +(module + (memory i64 1) + (data "\aa\bb\cc\dd") + + (func (export "init") (param i64 i32 i32) + (memory.init 0 + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0))) +) + +(invoke "init" (i64.const 0) (i32.const 1) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 0)) + +;; Init ending at memory limit and segment limit is ok. +(invoke "init" (i64.const 0xfffc) (i32.const 0) (i32.const 4)) + +;; Out-of-bounds writes trap, and no partial writes has been made. +(assert_trap (invoke "init" (i64.const 0xfffe) (i32.const 0) (i32.const 3)) + "out of bounds") +(assert_return (invoke "load8_u" (i64.const 0xfffe)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i64.const 0xffff)) (i32.const 0xdd)) + +;; Succeed when writing 0 bytes at the end of either region. +(invoke "init" (i64.const 0x10000) (i32.const 0) (i32.const 0)) +(invoke "init" (i64.const 0) (i32.const 4) (i32.const 0)) + +;; Writing 0 bytes outside of memory / segment limit is NOT allowed. +(assert_trap + (invoke "init" (i64.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds") +(assert_trap + (invoke "init" (i64.const 0) (i32.const 5) (i32.const 0)) + "out of bounds") + +;; OK to access 0 bytes at offset 0 in a dropped segment. +(invoke "init" (i64.const 0) (i32.const 0) (i32.const 0)) + +;; data.drop +(module + (memory i64 1) + (data "") + (data (i64.const 0) "") + + (func (export "drop_passive") (data.drop 0)) + (func (export "init_passive") + (memory.init 0 (i64.const 0) (i32.const 0) (i32.const 0))) + + (func (export "drop_active") (data.drop 1)) + (func (export "init_active") + (memory.init 1 (i64.const 0) (i32.const 0) (i32.const 0))) +) + +;; OK to drop the same segment multiple times or drop an active segment. +(invoke "init_passive") +(invoke "drop_passive") +(invoke "drop_passive") +(invoke "drop_active") diff --git a/test/core/call_indirect.wast b/test/core/call_indirect.wast index 79b8dc393..cc26b1b04 100644 --- a/test/core/call_indirect.wast +++ b/test/core/call_indirect.wast @@ -61,10 +61,15 @@ ) ) + (table $t64 i64 funcref + (elem $const-i32) + ) + ;; Syntax (func (call_indirect (i32.const 0)) + (call_indirect $t64 (i64.const 0)) (call_indirect (param i64) (i64.const 0) (i32.const 0)) (call_indirect (param i64) (param) (param f64 i32 i64) (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i32.const 0) @@ -94,6 +99,9 @@ (func (export "type-i32") (result i32) (call_indirect (type $out-i32) (i32.const 0)) ) + (func (export "type-i32-t64") (result i32) + (call_indirect $t64 (type $out-i32) (i64.const 0)) + ) (func (export "type-i64") (result i64) (call_indirect (type $out-i64) (i32.const 1)) ) @@ -474,6 +482,8 @@ (assert_return (invoke "type-f64") (f64.const 0xf64)) (assert_return (invoke "type-f64-i32") (f64.const 0xf64) (i32.const 32)) +(assert_return (invoke "type-i32-t64") (i32.const 0x132)) + (assert_return (invoke "type-index") (i64.const 100)) (assert_return (invoke "type-first-i32") (i32.const 32)) @@ -990,6 +1000,15 @@ "type mismatch" ) +;; call_indirect expects funcref type but receives externref +(assert_invalid + (module + (type (func)) + (table 10 externref) + (func $call-indirect (call_indirect (type 0) (i32.const 0))) + ) + "type mismatch" +) ;; Unbound type @@ -1008,6 +1027,20 @@ "unknown type" ) +;; pass very large number to call_indirect +(assert_invalid + (module + (type (func (param i32))) + (table 1 funcref) + (func $conditional-dangling-type + (if (i32.const 1) + (then (call_indirect (type 0xffffffff) (i32.const 0))) + ) + ) + ) + "unknown type" +) + ;; Unbound function in table diff --git a/test/core/call_ref.wast b/test/core/call_ref.wast index da480a7f2..aa9ac7b87 100644 --- a/test/core/call_ref.wast +++ b/test/core/call_ref.wast @@ -206,3 +206,13 @@ ) "type mismatch" ) + +(assert_invalid + (module + (type $t (func)) + (func $f (param $r funcref) + (call_ref $t (local.get $r)) + ) + ) + "type mismatch" +) diff --git a/test/core/elem.wast b/test/core/elem.wast index 7084e7901..1722a30b8 100644 --- a/test/core/elem.wast +++ b/test/core/elem.wast @@ -242,6 +242,352 @@ ) +;; Binary format variations + +(module + (func) + (table 1 funcref) + (elem (i32.const 0) func 0) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\07\01" ;; Elem section: 1 element segment + "\00\41\00\0b\01\00" ;; Segment 0: (i32.const 0) func 0 + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 funcref) + (elem func 0) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\05\01" ;; Elem section: 1 element segment + "\01\00\01\00" ;; Segment 0: func 0 + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 funcref) + (elem (table 0) (i32.const 0) func 0) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\09\01" ;; Elem section: 1 element segment + "\02\00\41\00\0b\00\01\00" ;; Segment 0: (table 0) (i32.const 0) func 0 + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 funcref) + (elem declare func 0) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\05\01" ;; Elem section: 1 element segment + "\03\00\01\00" ;; Segment 0: declare func 0 + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 funcref) + (elem (i32.const 0) (;;)(ref func) (ref.func 0)) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\09\01" ;; Elem section: 1 element segment + "\04\41\00\0b\01\d2\00\0b" ;; Segment 0: (i32.const 0) (ref.func 0) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) +(module + (func) + (table 1 funcref) + (elem (i32.const 0) funcref (ref.null func)) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\09\01" ;; Elem section: 1 element segment + "\04\41\00\0b\01\d0\70\0b" ;; Segment 0: (i32.const 0) (ref.null func) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 funcref) + (elem (i32.const 0) funcref (ref.func 0)) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\07\01" ;; Elem section: 1 element segment + "\05\70\01\d2\00\0b" ;; Segment 0: funcref (ref.func 0) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) +(module + (func) + (table 1 funcref) + (elem (i32.const 0) funcref (ref.null func)) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\07\01" ;; Elem section: 1 element segment + "\05\70\01\d0\70\0b" ;; Segment 0: funcref (ref.null func) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 funcref) + (elem (table 0) (i32.const 0) funcref (ref.func 0)) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\0b\01" ;; Elem section: 1 element segment + "\06\00\41\00\0b\70\01\d2\00\0b" ;; Segment 0: (table 0) (i32.const 0) funcref (ref.func 0) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) +(module + (func) + (table 1 funcref) + (elem (table 0) (i32.const 0) funcref (ref.null func)) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\0b\01" ;; Elem section: 1 element segment + "\06\00\41\00\0b\70\01\d0\70\0b" ;; Segment 0: (table 0) (i32.const 0) funcref (ref.null func) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 funcref) + (elem declare funcref (ref.func 0)) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\07\01" ;; Elem section: 1 element segment + "\07\70\01\d2\00\0b" ;; Segment 0: declare funcref (ref.func 0) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) +(module + (func) + (table 1 funcref) + (elem declare funcref (ref.null func)) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\04\01" ;; Table section: 1 table + "\70\00\01" ;; Table 0: [1..] funcref + "\09\07\01" ;; Elem section: 1 element segment + "\07\70\01\d0\70\0b" ;; Segment 0: declare funcref (ref.null func) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + + +(module + (func) + (table 1 (ref func) (ref.func 0)) + (elem (i32.const 0) func 0) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\0a\01" ;; Table section: 1 table + "\40\00\64\70\00\01\d2\00\0b" ;; Table 0: [1..] (ref func) (ref.func 0) + "\09\07\01" ;; Elem section: 1 element segment + "\00\41\00\0b\01\00" ;; Segment 0: (i32.const 0) func 0 + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 (ref func) (ref.func 0)) + (elem func 0) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\0a\01" ;; Table section: 1 table + "\40\00\64\70\00\01\d2\00\0b" ;; Table 0: [1..] (ref func) (ref.func 0) + "\09\05\01" ;; Elem section: 1 element segment + "\01\00\01\00" ;; Segment 0: func 0 + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 (ref func) (ref.func 0)) + (elem (table 0) (i32.const 0) func 0) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\0a\01" ;; Table section: 1 table + "\40\00\64\70\00\01\d2\00\0b" ;; Table 0: [1..] (ref func) (ref.func 0) + "\09\09\01" ;; Elem section: 1 element segment + "\02\00\41\00\0b\00\01\00" ;; Segment 0: (table 0) (i32.const 0) func 0 + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 (ref func) (ref.func 0)) + (elem declare func 0) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\0a\01" ;; Table section: 1 table + "\40\00\64\70\00\01\d2\00\0b" ;; Table 0: [1..] (ref func) (ref.func 0) + "\09\05\01" ;; Elem section: 1 element segment + "\03\00\01\00" ;; Segment 0: declare func 0 + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(assert_invalid + (module + (func) + (table 1 (ref func) (ref.func 0)) + (elem (i32.const 0) funcref (ref.func 0)) + ) + "type mismatch" +) +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\0a\01" ;; Table section: 1 table + "\40\00\64\70\00\01\d2\00\0b" ;; Table 0: [1..] (ref func) (ref.func 0) + "\09\09\01" ;; Elem section: 1 element segment + "\04\41\00\0b\01\d2\00\0b" ;; Segment 0: (i32.const 0) (ref.func 0) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty + ) + "type mismatch" +) + +(module + (func) + (table 1 (ref func) (ref.func 0)) + (elem (ref func) (ref.func 0)) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\0a\01" ;; Table section: 1 table + "\40\00\64\70\00\01\d2\00\0b" ;; Table 0: [1..] (ref func) (ref.func 0) + "\09\08\01" ;; Elem section: 1 element segment + "\05\64\70\01\d2\00\0b" ;; Segment 0: (ref func) (ref.func 0) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 (ref func) (ref.func 0)) + (elem (table 0) (i32.const 0) (ref func) (ref.func 0)) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\0a\01" ;; Table section: 1 table + "\40\00\64\70\00\01\d2\00\0b" ;; Table 0: [1..] (ref func) (ref.func 0) + "\09\0c\01" ;; Elem section: 1 element segment + "\06\00\41\00\0b\64\70\01\d2\00\0b" ;; Segment 0: (table 0) (i32.const 0) (ref func) (ref.func 0) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + +(module + (func) + (table 1 (ref func) (ref.func 0)) + (elem declare (ref func) (ref.func 0)) +) +(module binary + "\00asm" "\01\00\00\00" ;; Magic + "\01\04\01\60\00\00" ;; Type section: 1 type + "\03\02\01\00" ;; Function section: 1 function + "\04\0a\01" ;; Table section: 1 table + "\40\00\64\70\00\01\d2\00\0b" ;; Table 0: [1..] (ref func) (ref.func 0) + "\09\08\01" ;; Elem section: 1 element segment + "\07\64\70\01\d2\00\0b" ;; Segment 0: declare (ref func) (ref.func 0) + "\0a\04\01" ;; Code section: 1 function + "\02\00\0b" ;; Function 0: empty +) + + ;; Invalid bounds for elements (assert_trap @@ -659,11 +1005,11 @@ ;; Initializing a table with an externref-type element segment (module $m - (table $t (export "table") 2 externref) - (func (export "get") (param $i i32) (result externref) - (table.get $t (local.get $i))) - (func (export "set") (param $i i32) (param $x externref) - (table.set $t (local.get $i) (local.get $x)))) + (table $t (export "table") 2 externref) + (func (export "get") (param $i i32) (result externref) + (table.get $t (local.get $i))) + (func (export "set") (param $i i32) (param $x externref) + (table.set $t (local.get $i) (local.get $x)))) (register "exporter" $m) diff --git a/test/core/endianness64.wast b/test/core/endianness64.wast new file mode 100644 index 000000000..e583ea9b4 --- /dev/null +++ b/test/core/endianness64.wast @@ -0,0 +1,217 @@ +(module + (memory i64 1) + + ;; Stores an i16 value in little-endian-format + (func $i16_store_little (param $address i64) (param $value i32) + (i32.store8 (local.get $address) (local.get $value)) + (i32.store8 (i64.add (local.get $address) (i64.const 1)) (i32.shr_u (local.get $value) (i32.const 8))) + ) + + ;; Stores an i32 value in little-endian format + (func $i32_store_little (param $address i64) (param $value i32) + (call $i16_store_little (local.get $address) (local.get $value)) + (call $i16_store_little (i64.add (local.get $address) (i64.const 2)) (i32.shr_u (local.get $value) (i32.const 16))) + ) + + ;; Stores an i64 value in little-endian format + (func $i64_store_little (param $address i64) (param $value i64) + (call $i32_store_little (local.get $address) (i32.wrap_i64 (local.get $value))) + (call $i32_store_little (i64.add (local.get $address) (i64.const 4)) (i32.wrap_i64 (i64.shr_u (local.get $value) (i64.const 32)))) + ) + + ;; Loads an i16 value in little-endian format + (func $i16_load_little (param $address i64) (result i32) + (i32.or + (i32.load8_u (local.get $address)) + (i32.shl (i32.load8_u (i64.add (local.get $address) (i64.const 1))) (i32.const 8)) + ) + ) + + ;; Loads an i32 value in little-endian format + (func $i32_load_little (param $address i64) (result i32) + (i32.or + (call $i16_load_little (local.get $address)) + (i32.shl (call $i16_load_little (i64.add (local.get $address) (i64.const 2))) (i32.const 16)) + ) + ) + + ;; Loads an i64 value in little-endian format + (func $i64_load_little (param $address i64) (result i64) + (i64.or + (i64.extend_i32_u (call $i32_load_little (local.get $address))) + (i64.shl (i64.extend_i32_u (call $i32_load_little (i64.add (local.get $address) (i64.const 4)))) (i64.const 32)) + ) + ) + + (func (export "i32_load16_s") (param $value i32) (result i32) + (call $i16_store_little (i64.const 0) (local.get $value)) + (i32.load16_s (i64.const 0)) + ) + + (func (export "i32_load16_u") (param $value i32) (result i32) + (call $i16_store_little (i64.const 0) (local.get $value)) + (i32.load16_u (i64.const 0)) + ) + + (func (export "i32_load") (param $value i32) (result i32) + (call $i32_store_little (i64.const 0) (local.get $value)) + (i32.load (i64.const 0)) + ) + + (func (export "i64_load16_s") (param $value i64) (result i64) + (call $i16_store_little (i64.const 0) (i32.wrap_i64 (local.get $value))) + (i64.load16_s (i64.const 0)) + ) + + (func (export "i64_load16_u") (param $value i64) (result i64) + (call $i16_store_little (i64.const 0) (i32.wrap_i64 (local.get $value))) + (i64.load16_u (i64.const 0)) + ) + + (func (export "i64_load32_s") (param $value i64) (result i64) + (call $i32_store_little (i64.const 0) (i32.wrap_i64 (local.get $value))) + (i64.load32_s (i64.const 0)) + ) + + (func (export "i64_load32_u") (param $value i64) (result i64) + (call $i32_store_little (i64.const 0) (i32.wrap_i64 (local.get $value))) + (i64.load32_u (i64.const 0)) + ) + + (func (export "i64_load") (param $value i64) (result i64) + (call $i64_store_little (i64.const 0) (local.get $value)) + (i64.load (i64.const 0)) + ) + + (func (export "f32_load") (param $value f32) (result f32) + (call $i32_store_little (i64.const 0) (i32.reinterpret_f32 (local.get $value))) + (f32.load (i64.const 0)) + ) + + (func (export "f64_load") (param $value f64) (result f64) + (call $i64_store_little (i64.const 0) (i64.reinterpret_f64 (local.get $value))) + (f64.load (i64.const 0)) + ) + + + (func (export "i32_store16") (param $value i32) (result i32) + (i32.store16 (i64.const 0) (local.get $value)) + (call $i16_load_little (i64.const 0)) + ) + + (func (export "i32_store") (param $value i32) (result i32) + (i32.store (i64.const 0) (local.get $value)) + (call $i32_load_little (i64.const 0)) + ) + + (func (export "i64_store16") (param $value i64) (result i64) + (i64.store16 (i64.const 0) (local.get $value)) + (i64.extend_i32_u (call $i16_load_little (i64.const 0))) + ) + + (func (export "i64_store32") (param $value i64) (result i64) + (i64.store32 (i64.const 0) (local.get $value)) + (i64.extend_i32_u (call $i32_load_little (i64.const 0))) + ) + + (func (export "i64_store") (param $value i64) (result i64) + (i64.store (i64.const 0) (local.get $value)) + (call $i64_load_little (i64.const 0)) + ) + + (func (export "f32_store") (param $value f32) (result f32) + (f32.store (i64.const 0) (local.get $value)) + (f32.reinterpret_i32 (call $i32_load_little (i64.const 0))) + ) + + (func (export "f64_store") (param $value f64) (result f64) + (f64.store (i64.const 0) (local.get $value)) + (f64.reinterpret_i64 (call $i64_load_little (i64.const 0))) + ) +) + +(assert_return (invoke "i32_load16_s" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "i32_load16_s" (i32.const -4242)) (i32.const -4242)) +(assert_return (invoke "i32_load16_s" (i32.const 42)) (i32.const 42)) +(assert_return (invoke "i32_load16_s" (i32.const 0x3210)) (i32.const 0x3210)) + +(assert_return (invoke "i32_load16_u" (i32.const -1)) (i32.const 0xFFFF)) +(assert_return (invoke "i32_load16_u" (i32.const -4242)) (i32.const 61294)) +(assert_return (invoke "i32_load16_u" (i32.const 42)) (i32.const 42)) +(assert_return (invoke "i32_load16_u" (i32.const 0xCAFE)) (i32.const 0xCAFE)) + +(assert_return (invoke "i32_load" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "i32_load" (i32.const -42424242)) (i32.const -42424242)) +(assert_return (invoke "i32_load" (i32.const 42424242)) (i32.const 42424242)) +(assert_return (invoke "i32_load" (i32.const 0xABAD1DEA)) (i32.const 0xABAD1DEA)) + +(assert_return (invoke "i64_load16_s" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load16_s" (i64.const -4242)) (i64.const -4242)) +(assert_return (invoke "i64_load16_s" (i64.const 42)) (i64.const 42)) +(assert_return (invoke "i64_load16_s" (i64.const 0x3210)) (i64.const 0x3210)) + +(assert_return (invoke "i64_load16_u" (i64.const -1)) (i64.const 0xFFFF)) +(assert_return (invoke "i64_load16_u" (i64.const -4242)) (i64.const 61294)) +(assert_return (invoke "i64_load16_u" (i64.const 42)) (i64.const 42)) +(assert_return (invoke "i64_load16_u" (i64.const 0xCAFE)) (i64.const 0xCAFE)) + +(assert_return (invoke "i64_load32_s" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load32_s" (i64.const -42424242)) (i64.const -42424242)) +(assert_return (invoke "i64_load32_s" (i64.const 42424242)) (i64.const 42424242)) +(assert_return (invoke "i64_load32_s" (i64.const 0x12345678)) (i64.const 0x12345678)) + +(assert_return (invoke "i64_load32_u" (i64.const -1)) (i64.const 0xFFFFFFFF)) +(assert_return (invoke "i64_load32_u" (i64.const -42424242)) (i64.const 4252543054)) +(assert_return (invoke "i64_load32_u" (i64.const 42424242)) (i64.const 42424242)) +(assert_return (invoke "i64_load32_u" (i64.const 0xABAD1DEA)) (i64.const 0xABAD1DEA)) + +(assert_return (invoke "i64_load" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load" (i64.const -42424242)) (i64.const -42424242)) +(assert_return (invoke "i64_load" (i64.const 0xABAD1DEA)) (i64.const 0xABAD1DEA)) +(assert_return (invoke "i64_load" (i64.const 0xABADCAFEDEAD1DEA)) (i64.const 0xABADCAFEDEAD1DEA)) + +(assert_return (invoke "f32_load" (f32.const -1)) (f32.const -1)) +(assert_return (invoke "f32_load" (f32.const 1234e-5)) (f32.const 1234e-5)) +(assert_return (invoke "f32_load" (f32.const 4242.4242)) (f32.const 4242.4242)) +(assert_return (invoke "f32_load" (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) + +(assert_return (invoke "f64_load" (f64.const -1)) (f64.const -1)) +(assert_return (invoke "f64_load" (f64.const 123456789e-5)) (f64.const 123456789e-5)) +(assert_return (invoke "f64_load" (f64.const 424242.424242)) (f64.const 424242.424242)) +(assert_return (invoke "f64_load" (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) + + +(assert_return (invoke "i32_store16" (i32.const -1)) (i32.const 0xFFFF)) +(assert_return (invoke "i32_store16" (i32.const -4242)) (i32.const 61294)) +(assert_return (invoke "i32_store16" (i32.const 42)) (i32.const 42)) +(assert_return (invoke "i32_store16" (i32.const 0xCAFE)) (i32.const 0xCAFE)) + +(assert_return (invoke "i32_store" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "i32_store" (i32.const -4242)) (i32.const -4242)) +(assert_return (invoke "i32_store" (i32.const 42424242)) (i32.const 42424242)) +(assert_return (invoke "i32_store" (i32.const 0xDEADCAFE)) (i32.const 0xDEADCAFE)) + +(assert_return (invoke "i64_store16" (i64.const -1)) (i64.const 0xFFFF)) +(assert_return (invoke "i64_store16" (i64.const -4242)) (i64.const 61294)) +(assert_return (invoke "i64_store16" (i64.const 42)) (i64.const 42)) +(assert_return (invoke "i64_store16" (i64.const 0xCAFE)) (i64.const 0xCAFE)) + +(assert_return (invoke "i64_store32" (i64.const -1)) (i64.const 0xFFFFFFFF)) +(assert_return (invoke "i64_store32" (i64.const -4242)) (i64.const 4294963054)) +(assert_return (invoke "i64_store32" (i64.const 42424242)) (i64.const 42424242)) +(assert_return (invoke "i64_store32" (i64.const 0xDEADCAFE)) (i64.const 0xDEADCAFE)) + +(assert_return (invoke "i64_store" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_store" (i64.const -42424242)) (i64.const -42424242)) +(assert_return (invoke "i64_store" (i64.const 0xABAD1DEA)) (i64.const 0xABAD1DEA)) +(assert_return (invoke "i64_store" (i64.const 0xABADCAFEDEAD1DEA)) (i64.const 0xABADCAFEDEAD1DEA)) + +(assert_return (invoke "f32_store" (f32.const -1)) (f32.const -1)) +(assert_return (invoke "f32_store" (f32.const 1234e-5)) (f32.const 1234e-5)) +(assert_return (invoke "f32_store" (f32.const 4242.4242)) (f32.const 4242.4242)) +(assert_return (invoke "f32_store" (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) + +(assert_return (invoke "f64_store" (f64.const -1)) (f64.const -1)) +(assert_return (invoke "f64_store" (f64.const 123456789e-5)) (f64.const 123456789e-5)) +(assert_return (invoke "f64_store" (f64.const 424242.424242)) (f64.const 424242.424242)) +(assert_return (invoke "f64_store" (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) diff --git a/test/core/float_exprs.wast b/test/core/float_exprs.wast index 1f8829921..274c99665 100644 --- a/test/core/float_exprs.wast +++ b/test/core/float_exprs.wast @@ -649,7 +649,8 @@ (assert_return (invoke "no_fold_promote_demote" (f32.const inf)) (f32.const inf)) (assert_return (invoke "no_fold_promote_demote" (f32.const -inf)) (f32.const -inf)) -;; Test that demote(x+promote(y)) is not folded to demote(x)+y. +;; Test that demote(x+promote(y)) is not folded to demote(x)+y, and that +;; demote(promote(y)+x) is not folded to y+demote(x). (module (func (export "no_demote_mixed_add") (param $x f64) (param $y f32) (result f32) @@ -670,11 +671,14 @@ (assert_return (invoke "no_demote_mixed_add_commuted" (f32.const 0x1.096f4ap-29) (f64.const -0x1.0d5110e3385bbp-20)) (f32.const -0x1.0ccc5ap-20)) (assert_return (invoke "no_demote_mixed_add_commuted" (f32.const -0x1.24e474p-41) (f64.const -0x1.73852db4e5075p-20)) (f32.const -0x1.738536p-20)) -;; Test that demote(x-promote(y)) is not folded to demote(x)-y. +;; Test that demote(x-promote(y)) is not folded to demote(x)-y, and that +;; demote(promote(y)-x) is not folded to y-demote(x). (module (func (export "no_demote_mixed_sub") (param $x f64) (param $y f32) (result f32) (f32.demote_f64 (f64.sub (local.get $x) (f64.promote_f32 (local.get $y))))) + (func (export "no_demote_mixed_sub_commuted") (param $y f32) (param $x f64) (result f32) + (f32.demote_f64 (f64.sub (f64.promote_f32 (local.get $y)) (local.get $x)))) ) (assert_return (invoke "no_demote_mixed_sub" (f64.const 0x1.a0a183220e9b1p+82) (f32.const 0x1.c5acf8p+61)) (f32.const 0x1.a0a174p+82)) @@ -683,6 +687,56 @@ (assert_return (invoke "no_demote_mixed_sub" (f64.const 0x1.0459f34091dbfp-54) (f32.const 0x1.61ad08p-71)) (f32.const 0x1.045942p-54)) (assert_return (invoke "no_demote_mixed_sub" (f64.const 0x1.a7498dca3fdb7p+14) (f32.const 0x1.ed21c8p+15)) (f32.const -0x1.197d02p+15)) +(assert_return (invoke "no_demote_mixed_sub_commuted" (f32.const 0x1.c5acf8p+61) (f64.const 0x1.a0a183220e9b1p+82)) (f32.const -0x1.a0a174p+82)) +(assert_return (invoke "no_demote_mixed_sub_commuted" (f32.const 0x1.d48ca4p+17) (f64.const -0x1.6e2c5ac39f63ep+30)) (f32.const 0x1.6e3bp+30)) +(assert_return (invoke "no_demote_mixed_sub_commuted" (f32.const 0x1.9d69bcp-12) (f64.const -0x1.98c74350dde6ap+6)) (f32.const 0x1.98c7aap+6)) +(assert_return (invoke "no_demote_mixed_sub_commuted" (f32.const 0x1.61ad08p-71) (f64.const 0x1.0459f34091dbfp-54)) (f32.const -0x1.045942p-54)) +(assert_return (invoke "no_demote_mixed_sub_commuted" (f32.const 0x1.ed21c8p+15) (f64.const 0x1.a7498dca3fdb7p+14)) (f32.const 0x1.197d02p+15)) + +;; Test that demote(x*promote(y)) is not folded to demote(x)*y, and that +;; demote(promote(y)*x) is not folded to y*demote(x). + +(module + (func (export "no_demote_mixed_mul") (param $x f64) (param $y f32) (result f32) + (f32.demote_f64 (f64.mul (local.get $x) (f64.promote_f32 (local.get $y))))) + (func (export "no_demote_mixed_mul_commuted") (param $y f32) (param $x f64) (result f32) + (f32.demote_f64 (f64.mul (f64.promote_f32 (local.get $y)) (local.get $x)))) +) + +(assert_return (invoke "no_demote_mixed_mul" (f64.const 0x1.a19789e5aa475p-202) (f32.const 0x1.858cbep+113)) (f32.const 0x1.3db86cp-88)) +(assert_return (invoke "no_demote_mixed_mul" (f64.const 0x1.8f0e6a5a53f15p+140) (f32.const 0x1.2ef826p-107)) (f32.const 0x1.d845d2p+33)) +(assert_return (invoke "no_demote_mixed_mul" (f64.const 0x1.f03aa769e296cp+176) (f32.const 0x1.a9255p-57)) (f32.const 0x1.9c0cdap+120)) +(assert_return (invoke "no_demote_mixed_mul" (f64.const 0x1.9cd70b636bc52p+221) (f32.const 0x1.3f3ac6p-122)) (f32.const 0x1.01676p+100)) +(assert_return (invoke "no_demote_mixed_mul" (f64.const 0x1.c56b4c2991a3cp-170) (f32.const 0x1.1ad242p+48)) (f32.const 0x1.f4ec98p-122)) + +(assert_return (invoke "no_demote_mixed_mul_commuted" (f32.const 0x1.858cbep+113) (f64.const 0x1.a19789e5aa475p-202)) (f32.const 0x1.3db86cp-88)) +(assert_return (invoke "no_demote_mixed_mul_commuted" (f32.const 0x1.2ef826p-107) (f64.const 0x1.8f0e6a5a53f15p+140)) (f32.const 0x1.d845d2p+33)) +(assert_return (invoke "no_demote_mixed_mul_commuted" (f32.const 0x1.a9255p-57) (f64.const 0x1.f03aa769e296cp+176)) (f32.const 0x1.9c0cdap+120)) +(assert_return (invoke "no_demote_mixed_mul_commuted" (f32.const 0x1.3f3ac6p-122) (f64.const 0x1.9cd70b636bc52p+221)) (f32.const 0x1.01676p+100)) +(assert_return (invoke "no_demote_mixed_mul_commuted" (f32.const 0x1.1ad242p+48) (f64.const 0x1.c56b4c2991a3cp-170)) (f32.const 0x1.f4ec98p-122)) + +;; Test that demote(x/promote(y)) is not folded to demote(x)/y, and that +;; demote(promote(y)/x) is not folded to y/demote(x). + +(module + (func (export "no_demote_mixed_div") (param $x f64) (param $y f32) (result f32) + (f32.demote_f64 (f64.div (local.get $x) (f64.promote_f32 (local.get $y))))) + (func (export "no_demote_mixed_div_commuted") (param $y f32) (param $x f64) (result f32) + (f32.demote_f64 (f64.div (f64.promote_f32 (local.get $y)) (local.get $x)))) +) + +(assert_return (invoke "no_demote_mixed_div" (f64.const 0x1.40d0b55d4cee1p+150) (f32.const 0x1.6c7496p+103)) (f32.const 0x1.c2b158p+46)) +(assert_return (invoke "no_demote_mixed_div" (f64.const 0x1.402750f34cd98p-153) (f32.const 0x1.3db8ep-82)) (f32.const 0x1.01f586p-71)) +(assert_return (invoke "no_demote_mixed_div" (f64.const 0x1.3f7ece1a790a7p-37) (f32.const 0x1.a5652p-128)) (f32.const 0x1.8430dp+90)) +(assert_return (invoke "no_demote_mixed_div" (f64.const 0x1.5171328e16885p-138) (f32.const 0x1.10636ap-88)) (f32.const 0x1.3d23cep-50)) +(assert_return (invoke "no_demote_mixed_div" (f64.const 0x1.d3a380fc986ccp+74) (f32.const 0x1.f095b6p+88)) (f32.const 0x1.e227c4p-15)) + +(assert_return (invoke "no_demote_mixed_div_commuted" (f32.const 0x1.d78ddcp-74) (f64.const 0x1.2c57e125069e2p-42)) (f32.const 0x1.91eed6p-32)) +(assert_return (invoke "no_demote_mixed_div_commuted" (f32.const 0x1.7db224p+26) (f64.const 0x1.1c291ec609ed4p+159)) (f32.const 0x1.57dfp-133)) +(assert_return (invoke "no_demote_mixed_div_commuted" (f32.const 0x1.e7a824p-40) (f64.const 0x1.f4bdb25ff00fcp-137)) (f32.const 0x1.f29f22p+96)) +(assert_return (invoke "no_demote_mixed_div_commuted" (f32.const 0x1.730b8p+80) (f64.const 0x1.880fb331a64cap+210)) (f32.const 0x1.e48ep-131)) +(assert_return (invoke "no_demote_mixed_div_commuted" (f32.const 0x1.7715fcp-73) (f64.const 0x1.6feb1fa66f11bp-198)) (f32.const 0x1.04fcb6p+125)) + ;; Test that converting between integer and float and back isn't folded away. (module @@ -1986,7 +2040,7 @@ (assert_return (invoke "f64.xkcd_better_sqrt_5" (f64.const 13.0) (f64.const 4.0) (f64.const 0x1.921fb54442d18p+1) (f64.const 24.0)) (f64.const 0x1.1e3778509a5a3p+1)) ;; Compute the floating-point radix. -;; M. A. Malcom. Algorithms to reveal properties of floating-point arithmetic. +;; M. A. Malcolm. Algorithms to reveal properties of floating-point arithmetic. ;; Communications of the ACM, 15(11):949-951, November 1972. (module (func (export "f32.compute_radix") (param $0 f32) (param $1 f32) (result f32) diff --git a/test/core/float_memory64.wast b/test/core/float_memory64.wast new file mode 100644 index 000000000..674b254f5 --- /dev/null +++ b/test/core/float_memory64.wast @@ -0,0 +1,157 @@ +;; Test that floating-point load and store are bit-preserving. + +;; Test that load and store do not canonicalize NaNs as x87 does. + +(module + (memory i64 (data "\00\00\a0\7f")) + + (func (export "f32.load") (result f32) (f32.load (i64.const 0))) + (func (export "i32.load") (result i32) (i32.load (i64.const 0))) + (func (export "f32.store") (f32.store (i64.const 0) (f32.const nan:0x200000))) + (func (export "i32.store") (i32.store (i64.const 0) (i32.const 0x7fa00000))) + (func (export "reset") (i32.store (i64.const 0) (i32.const 0))) +) + +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "f32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "i32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) + +(module + (memory i64 (data "\00\00\00\00\00\00\f4\7f")) + + (func (export "f64.load") (result f64) (f64.load (i64.const 0))) + (func (export "i64.load") (result i64) (i64.load (i64.const 0))) + (func (export "f64.store") (f64.store (i64.const 0) (f64.const nan:0x4000000000000))) + (func (export "i64.store") (i64.store (i64.const 0) (i64.const 0x7ff4000000000000))) + (func (export "reset") (i64.store (i64.const 0) (i64.const 0))) +) + +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "f64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "i64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) + +;; Test that unaligned load and store do not canonicalize NaNs. + +(module + (memory i64 (data "\00\00\00\a0\7f")) + + (func (export "f32.load") (result f32) (f32.load (i64.const 1))) + (func (export "i32.load") (result i32) (i32.load (i64.const 1))) + (func (export "f32.store") (f32.store (i64.const 1) (f32.const nan:0x200000))) + (func (export "i32.store") (i32.store (i64.const 1) (i32.const 0x7fa00000))) + (func (export "reset") (i32.store (i64.const 1) (i32.const 0))) +) + +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "f32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "i32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) + +(module + (memory i64 (data "\00\00\00\00\00\00\00\f4\7f")) + + (func (export "f64.load") (result f64) (f64.load (i64.const 1))) + (func (export "i64.load") (result i64) (i64.load (i64.const 1))) + (func (export "f64.store") (f64.store (i64.const 1) (f64.const nan:0x4000000000000))) + (func (export "i64.store") (i64.store (i64.const 1) (i64.const 0x7ff4000000000000))) + (func (export "reset") (i64.store (i64.const 1) (i64.const 0))) +) + +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "f64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "i64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) + +;; Test that load and store do not canonicalize NaNs as some JS engines do. + +(module + (memory i64 (data "\01\00\d0\7f")) + + (func (export "f32.load") (result f32) (f32.load (i64.const 0))) + (func (export "i32.load") (result i32) (i32.load (i64.const 0))) + (func (export "f32.store") (f32.store (i64.const 0) (f32.const nan:0x500001))) + (func (export "i32.store") (i32.store (i64.const 0) (i32.const 0x7fd00001))) + (func (export "reset") (i32.store (i64.const 0) (i32.const 0))) +) + +(assert_return (invoke "i32.load") (i32.const 0x7fd00001)) +(assert_return (invoke "f32.load") (f32.const nan:0x500001)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "f32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fd00001)) +(assert_return (invoke "f32.load") (f32.const nan:0x500001)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "i32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fd00001)) +(assert_return (invoke "f32.load") (f32.const nan:0x500001)) + +(module + (memory i64 (data "\01\00\00\00\00\00\fc\7f")) + + (func (export "f64.load") (result f64) (f64.load (i64.const 0))) + (func (export "i64.load") (result i64) (i64.load (i64.const 0))) + (func (export "f64.store") (f64.store (i64.const 0) (f64.const nan:0xc000000000001))) + (func (export "i64.store") (i64.store (i64.const 0) (i64.const 0x7ffc000000000001))) + (func (export "reset") (i64.store (i64.const 0) (i64.const 0))) +) + +(assert_return (invoke "i64.load") (i64.const 0x7ffc000000000001)) +(assert_return (invoke "f64.load") (f64.const nan:0xc000000000001)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "f64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ffc000000000001)) +(assert_return (invoke "f64.load") (f64.const nan:0xc000000000001)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "i64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ffc000000000001)) +(assert_return (invoke "f64.load") (f64.const nan:0xc000000000001)) diff --git a/test/core/float_misc.wast b/test/core/float_misc.wast index 3d83281d7..a2153cfab 100644 --- a/test/core/float_misc.wast +++ b/test/core/float_misc.wast @@ -587,6 +587,50 @@ (assert_return (invoke "f64.sqrt" (f64.const 0x1.ffffffffffffep-1)) (f64.const 0x1.fffffffffffffp-1)) (assert_return (invoke "f64.sqrt" (f64.const 0x1.ffffffffffffdp-1)) (f64.const 0x1.ffffffffffffep-1)) +;; Test the values of sqrt around ¼, where `sqrt(x) - x` is greatest. +(assert_return (invoke "f32.sqrt" (f32.const 0x1.fffffep-3)) (f32.const 0x1.fffffep-2)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1p-2)) (f32.const 0x1p-1)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.000002p-2)) (f32.const 0x1p-1)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.000004p-2)) (f32.const 0x1.000002p-1)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.fffffffffffffp-3)) (f64.const 0x1.fffffffffffffp-2)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1p-2)) (f64.const 0x1p-1)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.0000000000001p-2)) (f64.const 0x1p-1)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.0000000000002p-2)) (f64.const 0x1.0000000000001p-1)) + +;; Test some values that in some systems differ between CPU and DSP. +;; https://e2e.ti.com/support/processors-group/processors/f/processors-forum/30725/sqrt-function-gives-slightly-different-results---may-be-rounding-problem +(assert_return (invoke "f32.sqrt" (f32.const 0x1.fb41d4p+37)) (f32.const 0x1.fd9f8p+18)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.fb41d442eeb1bp+37)) (f64.const 0x1.fd9f808a0b68dp+18)) + +;; Test some values that in some systems differ between CPU and GPU. +;; https://forums.developer.nvidia.com/t/sqrt-precision/18597 +(assert_return (invoke "f32.sqrt" (f32.const 0x1.3c61b2p+33)) (f32.const 0x1.927ap+16)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.56bd4ep+51)) (f32.const 0x1.a2e80cp+25)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.65f02cp+44)) (f32.const 0x1.2eb544p+22)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.26580cp+30)) (f32.const 0x1.1280d6p+15)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.3c61b112p+33)) (f64.const 0x1.927ap+16)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.56bd4e65c8548p+51)) (f64.const 0x1.a2e80dp+25)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.65f02cc93a1p+44)) (f64.const 0x1.2eb544p+22)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.26580b4cp+30)) (f64.const 0x1.1280d62b818cfp+15)) + +;; Test some values that in some systems differ between CPU and GPU. +;; https://github.com/pytorch/pytorch/issues/31250 +(assert_return (invoke "f32.sqrt" (f32.const 0x1.0817fcp-1)) (f32.const 0x1.6fb79ep-1)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.017b98p-1)) (f32.const 0x1.6b15eep-1)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.0817fcp-1)) (f64.const 0x1.6fb79d0dfaffap-1)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.017b98p-1)) (f64.const 0x1.6b15ed0071b95p-1)) + +;; Test that sqrt is not a "good enough" approximation. +;; https://sicp.sourceacademy.org/chapters/1.1.7.html +(assert_return (invoke "f32.sqrt" (f32.const 0x1.2p+3)) (f32.const 0x1.8p+1)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.12p+7)) (f32.const 0x1.768ce6p+3)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.c615dep+0)) (f32.const 0x1.54f2dp+0)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.f4p+9)) (f32.const 0x1.f9f6e4p+4)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.2p+3)) (f64.const 0x1.8p+1)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.12p+7)) (f64.const 0x1.768ce6d3c11ep+3)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.c615df07a57d3p+0)) (f64.const 0x1.54f2d015acf09p+0)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.f4p+9)) (f64.const 0x1.f9f6e4990f227p+4)) + ;; Test that the bitwise floating point operators are bitwise on NaN. (assert_return (invoke "f32.abs" (f32.const nan:0x0f1e2)) (f32.const nan:0x0f1e2)) diff --git a/test/core/gc/array.wast b/test/core/gc/array.wast index 6ad95c087..cebf20069 100644 --- a/test/core/gc/array.wast +++ b/test/core/gc/array.wast @@ -158,6 +158,10 @@ (array.new_data $vec $d (i32.const 1) (i32.const 3)) ) + (func $new-overflow (export "new-overflow") (result (ref $vec)) + (array.new_data $vec $d (i32.const 0x8000_0000) (i32.const 0x8000_0000)) + ) + (func $get_u (param $i i32) (param $v (ref $vec)) (result i32) (array.get_u $vec (local.get $v) (local.get $i)) ) @@ -189,6 +193,10 @@ (func (export "len") (result i32) (call $len (call $new)) ) + + (func (export "drop_segs") + (data.drop $d) + ) ) (assert_return (invoke "new") (ref.array)) @@ -198,10 +206,16 @@ (assert_return (invoke "set_get" (i32.const 1) (i32.const 7)) (i32.const 7)) (assert_return (invoke "len") (i32.const 3)) +(assert_trap (invoke "new-overflow") "out of bounds memory access") (assert_trap (invoke "get_u" (i32.const 10)) "out of bounds array access") (assert_trap (invoke "get_s" (i32.const 10)) "out of bounds array access") (assert_trap (invoke "set_get" (i32.const 10) (i32.const 7)) "out of bounds array access") +(assert_return (invoke "drop_segs")) + +(assert_trap (invoke "new") "out of bounds memory access") +(assert_trap (invoke "new-overflow") "out of bounds memory access") + (module (type $bvec (array i8)) (type $vec (array (ref $bvec))) @@ -218,6 +232,10 @@ (array.new_elem $vec $e (i32.const 0) (i32.const 2)) ) + (func $new-overflow (export "new-overflow") (result (ref $vec)) + (array.new_elem $vec $e (i32.const 0x8000_0000) (i32.const 0x8000_0000)) + ) + (func $sub1 (result (ref $nvec)) (array.new_elem $nvec $e (i32.const 0) (i32.const 2)) ) @@ -249,6 +267,10 @@ (func (export "len") (result i32) (call $len (call $new)) ) + + (func (export "drop_segs") + (elem.drop $e) + ) ) (assert_return (invoke "new") (ref.array)) @@ -258,9 +280,15 @@ (assert_return (invoke "set_get" (i32.const 0) (i32.const 1) (i32.const 1)) (i32.const 2)) (assert_return (invoke "len") (i32.const 2)) +(assert_trap (invoke "new-overflow") "out of bounds table access") (assert_trap (invoke "get" (i32.const 10) (i32.const 0)) "out of bounds array access") (assert_trap (invoke "set_get" (i32.const 10) (i32.const 0) (i32.const 0)) "out of bounds array access") +(assert_return (invoke "drop_segs")) + +(assert_trap (invoke "new") "out of bounds table access") +(assert_trap (invoke "new-overflow") "out of bounds table access") + (assert_invalid (module (type $a (array i64)) diff --git a/test/core/gc/array_new_data.wast b/test/core/gc/array_new_data.wast new file mode 100644 index 000000000..2af894c16 --- /dev/null +++ b/test/core/gc/array_new_data.wast @@ -0,0 +1,68 @@ +(module + (type $arr (array (mut i8))) + + (data $d "abcd") + + (func (export "array-new-data") (param i32 i32) (result (ref $arr)) + (array.new_data $arr $d (local.get 0) (local.get 1)) + ) +) + +;; In-bounds data segment accesses. +(assert_return (invoke "array-new-data" (i32.const 0) (i32.const 0)) (ref.array)) +(assert_return (invoke "array-new-data" (i32.const 0) (i32.const 4)) (ref.array)) +(assert_return (invoke "array-new-data" (i32.const 1) (i32.const 2)) (ref.array)) +(assert_return (invoke "array-new-data" (i32.const 4) (i32.const 0)) (ref.array)) + +;; Out-of-bounds data segment accesses. +(assert_trap (invoke "array-new-data" (i32.const 0) (i32.const 5)) "out of bounds memory access") +(assert_trap (invoke "array-new-data" (i32.const 5) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "array-new-data" (i32.const 1) (i32.const 4)) "out of bounds memory access") +(assert_trap (invoke "array-new-data" (i32.const 4) (i32.const 1)) "out of bounds memory access") + + +(module + (type $arr (array (mut i8))) + + (data $d "\aa\bb\cc\dd") + + (func (export "array-new-data-contents") (result i32 i32) + (local (ref $arr)) + (local.set 0 (array.new_data $arr $d (i32.const 1) (i32.const 2))) + (array.get_u $arr (local.get 0) (i32.const 0)) + (array.get_u $arr (local.get 0) (i32.const 1)) + ) +) + +;; Array is initialized with the correct contents. +(assert_return (invoke "array-new-data-contents") (i32.const 0xbb) (i32.const 0xcc)) + +(module + (type $arr (array (mut i32))) + + (data $d "\aa\bb\cc\dd") + + (func (export "array-new-data-little-endian") (result i32) + (array.get $arr + (array.new_data $arr $d (i32.const 0) (i32.const 1)) + (i32.const 0)) + ) +) + +;; Data segments are interpreted as little-endian. +(assert_return (invoke "array-new-data-little-endian") (i32.const 0xddccbbaa)) + +(module + (type $arr (array (mut i16))) + + (data $d "\00\11\22") + + (func (export "array-new-data-unaligned") (result i32) + (array.get_u $arr + (array.new_data $arr $d (i32.const 1) (i32.const 1)) + (i32.const 0)) + ) +) + +;; Data inside the segment doesn't need to be aligned to the element size. +(assert_return (invoke "array-new-data-unaligned") (i32.const 0x2211)) diff --git a/test/core/gc/array_new_elem.wast b/test/core/gc/array_new_elem.wast new file mode 100644 index 000000000..d71d981d6 --- /dev/null +++ b/test/core/gc/array_new_elem.wast @@ -0,0 +1,103 @@ +;;;; Expression-style element segments. + +(module + (type $arr (array i31ref)) + + (elem $e i31ref + (ref.i31 (i32.const 0xaa)) + (ref.i31 (i32.const 0xbb)) + (ref.i31 (i32.const 0xcc)) + (ref.i31 (i32.const 0xdd))) + + (func (export "array-new-elem") (param i32 i32) (result (ref $arr)) + (array.new_elem $arr $e (local.get 0) (local.get 1)) + ) +) + +;; In-bounds element segment accesses. +(assert_return (invoke "array-new-elem" (i32.const 0) (i32.const 0)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 0) (i32.const 4)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 1) (i32.const 2)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 4) (i32.const 0)) (ref.array)) + +;; Out-of-bounds element segment accesses. +(assert_trap (invoke "array-new-elem" (i32.const 0) (i32.const 5)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 5) (i32.const 0)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 1) (i32.const 4)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 4) (i32.const 1)) "out of bounds table access") + +(module + (type $arr (array i31ref)) + + (elem $e i31ref + (ref.i31 (i32.const 0xaa)) + (ref.i31 (i32.const 0xbb)) + (ref.i31 (i32.const 0xcc)) + (ref.i31 (i32.const 0xdd))) + + (func (export "array-new-elem-contents") (result i32 i32) + (local (ref $arr)) + (local.set 0 (array.new_elem $arr $e (i32.const 1) (i32.const 2))) + (i31.get_u (array.get $arr (local.get 0) (i32.const 0))) + (i31.get_u (array.get $arr (local.get 0) (i32.const 1))) + ) +) + +;; Array is initialized with the correct contents. +(assert_return (invoke "array-new-elem-contents") (i32.const 0xbb) (i32.const 0xcc)) + +;;;; MVP-style function-index segments. + +(module + (type $arr (array funcref)) + + (elem $e func $aa $bb $cc $dd) + (func $aa (result i32) (i32.const 0xaa)) + (func $bb (result i32) (i32.const 0xbb)) + (func $cc (result i32) (i32.const 0xcc)) + (func $dd (result i32) (i32.const 0xdd)) + + (func (export "array-new-elem") (param i32 i32) (result (ref $arr)) + (array.new_elem $arr $e (local.get 0) (local.get 1)) + ) +) + +;; In-bounds element segment accesses. +(assert_return (invoke "array-new-elem" (i32.const 0) (i32.const 0)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 0) (i32.const 4)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 1) (i32.const 2)) (ref.array)) +(assert_return (invoke "array-new-elem" (i32.const 4) (i32.const 0)) (ref.array)) + +;; Out-of-bounds element segment accesses. +(assert_trap (invoke "array-new-elem" (i32.const 0) (i32.const 5)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 5) (i32.const 0)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 1) (i32.const 4)) "out of bounds table access") +(assert_trap (invoke "array-new-elem" (i32.const 4) (i32.const 1)) "out of bounds table access") + +(module + (type $f (func (result i32))) + (type $arr (array funcref)) + + (elem $e func $aa $bb $cc $dd) + (func $aa (result i32) (i32.const 0xaa)) + (func $bb (result i32) (i32.const 0xbb)) + (func $cc (result i32) (i32.const 0xcc)) + (func $dd (result i32) (i32.const 0xdd)) + + (table $t 2 2 funcref) + + (func (export "array-new-elem-contents") (result i32 i32) + (local (ref $arr)) + (local.set 0 (array.new_elem $arr $e (i32.const 1) (i32.const 2))) + + (table.set $t (i32.const 0) (array.get $arr (local.get 0) (i32.const 0))) + (table.set $t (i32.const 1) (array.get $arr (local.get 0) (i32.const 1))) + + (call_indirect (type $f) (i32.const 0)) + (call_indirect (type $f) (i32.const 1)) + + ) +) + +;; Array is initialized with the correct contents. +(assert_return (invoke "array-new-elem-contents") (i32.const 0xbb) (i32.const 0xcc)) diff --git a/test/core/gc/i31.wast b/test/core/gc/i31.wast index 3b6c32fba..6309e72b6 100644 --- a/test/core/gc/i31.wast +++ b/test/core/gc/i31.wast @@ -14,15 +14,20 @@ (i31.get_u (ref.null i31)) ) (func (export "get_s-null") (result i32) - (i31.get_u (ref.null i31)) + (i31.get_s (ref.null i31)) ) (global $i (ref i31) (ref.i31 (i32.const 2))) (global $m (mut (ref i31)) (ref.i31 (i32.const 3))) + (func (export "get_globals") (result i32 i32) (i31.get_u (global.get $i)) (i31.get_u (global.get $m)) ) + + (func (export "set_global") (param i32) + (global.set $m (ref.i31 (local.get 0))) + ) ) (assert_return (invoke "new" (i32.const 1)) (ref.i31)) @@ -49,3 +54,175 @@ (assert_trap (invoke "get_s-null") "null i31 reference") (assert_return (invoke "get_globals") (i32.const 2) (i32.const 3)) + +(invoke "set_global" (i32.const 1234)) +(assert_return (invoke "get_globals") (i32.const 2) (i32.const 1234)) + +(module $tables_of_i31ref + (table $table 3 10 i31ref) + (elem (table $table) (i32.const 0) i31ref (item (ref.i31 (i32.const 999))) + (item (ref.i31 (i32.const 888))) + (item (ref.i31 (i32.const 777)))) + + (func (export "size") (result i32) + table.size $table + ) + + (func (export "get") (param i32) (result i32) + (i31.get_u (table.get $table (local.get 0))) + ) + + (func (export "grow") (param i32 i32) (result i32) + (table.grow $table (ref.i31 (local.get 1)) (local.get 0)) + ) + + (func (export "fill") (param i32 i32 i32) + (table.fill $table (local.get 0) (ref.i31 (local.get 1)) (local.get 2)) + ) + + (func (export "copy") (param i32 i32 i32) + (table.copy $table $table (local.get 0) (local.get 1) (local.get 2)) + ) + + (elem $elem i31ref (item (ref.i31 (i32.const 123))) + (item (ref.i31 (i32.const 456))) + (item (ref.i31 (i32.const 789)))) + (func (export "init") (param i32 i32 i32) + (table.init $table $elem (local.get 0) (local.get 1) (local.get 2)) + ) +) + +;; Initial state. +(assert_return (invoke "size") (i32.const 3)) +(assert_return (invoke "get" (i32.const 0)) (i32.const 999)) +(assert_return (invoke "get" (i32.const 1)) (i32.const 888)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 777)) + +;; Grow from size 3 to size 5. +(assert_return (invoke "grow" (i32.const 2) (i32.const 333)) (i32.const 3)) +(assert_return (invoke "size") (i32.const 5)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 333)) +(assert_return (invoke "get" (i32.const 4)) (i32.const 333)) + +;; Fill table[2..4] = 111. +(invoke "fill" (i32.const 2) (i32.const 111) (i32.const 2)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 111)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 111)) + +;; Copy from table[0..2] to table[3..5]. +(invoke "copy" (i32.const 3) (i32.const 0) (i32.const 2)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 999)) +(assert_return (invoke "get" (i32.const 4)) (i32.const 888)) + +;; Initialize the passive element at table[1..4]. +(invoke "init" (i32.const 1) (i32.const 0) (i32.const 3)) +(assert_return (invoke "get" (i32.const 1)) (i32.const 123)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 456)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 789)) + +(module $env + (global (export "g") i32 (i32.const 42)) +) +(register "env") + +(module $i31ref_of_global_table_initializer + (global $g (import "env" "g") i32) + (table $t 3 3 (ref i31) (ref.i31 (global.get $g))) + (func (export "get") (param i32) (result i32) + (i31.get_u (local.get 0) (table.get $t)) + ) +) + +(assert_return (invoke "get" (i32.const 0)) (i32.const 42)) +(assert_return (invoke "get" (i32.const 1)) (i32.const 42)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 42)) + +(module $i31ref_of_global_global_initializer + (global $g0 (import "env" "g") i32) + (global $g1 i31ref (ref.i31 (global.get $g0))) + (func (export "get") (result i32) + (i31.get_u (global.get $g1)) + ) +) + +(assert_return (invoke "get") (i32.const 42)) + +(module $anyref_global_of_i31ref + (global $c anyref (ref.i31 (i32.const 1234))) + (global $m (mut anyref) (ref.i31 (i32.const 5678))) + + (func (export "get_globals") (result i32 i32) + (i31.get_u (ref.cast i31ref (global.get $c))) + (i31.get_u (ref.cast i31ref (global.get $m))) + ) + + (func (export "set_global") (param i32) + (global.set $m (ref.i31 (local.get 0))) + ) +) + +(assert_return (invoke "get_globals") (i32.const 1234) (i32.const 5678)) +(invoke "set_global" (i32.const 0)) +(assert_return (invoke "get_globals") (i32.const 1234) (i32.const 0)) + +(module $anyref_table_of_i31ref + (table $table 3 10 anyref) + (elem (table $table) (i32.const 0) i31ref (item (ref.i31 (i32.const 999))) + (item (ref.i31 (i32.const 888))) + (item (ref.i31 (i32.const 777)))) + + (func (export "size") (result i32) + table.size $table + ) + + (func (export "get") (param i32) (result i32) + (i31.get_u (ref.cast i31ref (table.get $table (local.get 0)))) + ) + + (func (export "grow") (param i32 i32) (result i32) + (table.grow $table (ref.i31 (local.get 1)) (local.get 0)) + ) + + (func (export "fill") (param i32 i32 i32) + (table.fill $table (local.get 0) (ref.i31 (local.get 1)) (local.get 2)) + ) + + (func (export "copy") (param i32 i32 i32) + (table.copy $table $table (local.get 0) (local.get 1) (local.get 2)) + ) + + (elem $elem i31ref (item (ref.i31 (i32.const 123))) + (item (ref.i31 (i32.const 456))) + (item (ref.i31 (i32.const 789)))) + (func (export "init") (param i32 i32 i32) + (table.init $table $elem (local.get 0) (local.get 1) (local.get 2)) + ) +) + +;; Initial state. +(assert_return (invoke "size") (i32.const 3)) +(assert_return (invoke "get" (i32.const 0)) (i32.const 999)) +(assert_return (invoke "get" (i32.const 1)) (i32.const 888)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 777)) + +;; Grow from size 3 to size 5. +(assert_return (invoke "grow" (i32.const 2) (i32.const 333)) (i32.const 3)) +(assert_return (invoke "size") (i32.const 5)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 333)) +(assert_return (invoke "get" (i32.const 4)) (i32.const 333)) + +;; Fill table[2..4] = 111. +(invoke "fill" (i32.const 2) (i32.const 111) (i32.const 2)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 111)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 111)) + +;; Copy from table[0..2] to table[3..5]. +(invoke "copy" (i32.const 3) (i32.const 0) (i32.const 2)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 999)) +(assert_return (invoke "get" (i32.const 4)) (i32.const 888)) + +;; Initialize the passive element at table[1..4]. +(invoke "init" (i32.const 1) (i32.const 0) (i32.const 3)) +(assert_return (invoke "get" (i32.const 1)) (i32.const 123)) +(assert_return (invoke "get" (i32.const 2)) (i32.const 456)) +(assert_return (invoke "get" (i32.const 3)) (i32.const 789)) diff --git a/test/core/gc/type-subtyping.wast b/test/core/gc/type-subtyping.wast index a9022fc33..891aa31ad 100644 --- a/test/core/gc/type-subtyping.wast +++ b/test/core/gc/type-subtyping.wast @@ -316,6 +316,34 @@ (assert_trap (invoke "fail3") "cast") (assert_trap (invoke "fail4") "cast") +(module + (type $t1 (sub (func))) + (type $t2 (sub $t1 (func))) + (type $t3 (sub $t2 (func))) + (type $t4 (sub final (func))) + + (func $f2 (type $t2)) + (func $f3 (type $t3)) + (table (ref null $t2) (elem $f2 $f3)) + + (func (export "run") + (call_indirect (type $t1) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1)) + (call_indirect (type $t2) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1)) + (call_indirect (type $t3) (i32.const 1)) + ) + + (func (export "fail1") + (call_indirect (type $t3) (i32.const 0)) + ) + (func (export "fail2") + (call_indirect (type $t4) (i32.const 0)) + ) +) +(assert_return (invoke "run")) +(assert_trap (invoke "fail1") "indirect call") +(assert_trap (invoke "fail2") "indirect call") (module (rec (type $f1 (sub (func))) (type (struct (field (ref $f1))))) @@ -740,7 +768,7 @@ "sub type" ) -(assert_invalid +(assert_invalid (module (type $f0 (sub (func (param i32) (result i32)))) (type $s0 (sub $f0 (struct))) @@ -764,7 +792,7 @@ "sub type" ) -(assert_invalid +(assert_invalid (module (type $s0 (sub (struct))) (type $f0 (sub $s0 (func (param i32) (result i32)))) @@ -772,7 +800,7 @@ "sub type" ) -(assert_invalid +(assert_invalid (module (type $a0 (sub (array i32))) (type $f0 (sub $a0 (func (param i32) (result i32)))) @@ -796,6 +824,70 @@ "sub type" ) +(assert_invalid + (module + (type $a (sub (array (ref none)))) + (type $b (sub $a (array (ref any)))) + ) + "sub type 1 does not match super type" +) + +(assert_invalid + (module + (type $a (sub (array (mut (ref any))))) + (type $b (sub $a (array (mut (ref none))))) + ) + "sub type 1 does not match super type" +) + +(assert_invalid + (module + (type $a (sub (array (mut (ref any))))) + (type $b (sub $a (array (ref any)))) + ) + "sub type 1 does not match super type" +) + +(assert_invalid + (module + (type $a (sub (array (ref any)))) + (type $b (sub $a (array (mut (ref any))))) + ) + "sub type 1 does not match super type" +) + +(assert_invalid + (module + (type $a (sub (struct (field (ref none))))) + (type $b (sub $a (struct (field (ref any))))) + ) + "sub type 1 does not match super type" +) + +(assert_invalid + (module + (type $a (sub (struct (field (mut (ref any)))))) + (type $b (sub $a (struct (field (mut (ref none)))))) + ) + "sub type 1 does not match super type" +) + +(assert_invalid + (module + (type $a (sub (struct (field (mut (ref any)))))) + (type $b (sub $a (struct (field (ref any))))) + ) + "sub type 1 does not match super type" +) + +(assert_invalid + (module + (type $a (sub (struct (field (ref any))))) + (type $b (sub $a (struct (field (mut (ref any)))))) + ) + "sub type 1 does not match super type" +) + (assert_invalid (module (type $f0 (sub (func))) @@ -803,4 +895,3 @@ ) "sub type" ) - diff --git a/test/core/id.wast b/test/core/id.wast new file mode 100644 index 000000000..dcf151f22 --- /dev/null +++ b/test/core/id.wast @@ -0,0 +1,31 @@ +(module + (func $fg) (func (call $fg)) + (func $03) (func (call $03)) + (func $!?@#a$%^&*b-+_.:9'`|/\<=>~) (func (call $!?@#a$%^&*b-+_.:9'`|/\<=>~)) + (func $" random \t \n stuff ") (func (call $" random \t \n stuff ")) + (func $" ") (func (call $" ")) + + (func $fh) (func (call $"fh")) + (func $"fi") (func (call $fi)) + (func $!?@#a$%^&*-+_.:9'`|/\<=>~) (func (call $"!?@#a$%^&*-+_.:9'`|/\\<=>~")) + + (func $"\41B") (func (call $"AB") (call $"A\42") (call $"\41\42") (call $"\u{41}\u{42}")) + (func $"\t") (func (call $"\09") (call $"\u{09}")) + (func $"") (func (call $"\ef\98\9a\ef\92\a9") (call $"\u{f61a}\u{f4a9}")) + + (func + block $l1 (br $"l1") end $"l1" + block $007 (br $"007") end $"007" + block $!?@#a$%^&*-+_.:9'`|/\<=>~ end $"!?@#a$%^&*-+_.:9'`|/\\<=>~" + (i32.const 0) if $"\41B" (br $AB) else $"A\42" end $"\u{41}\u{42}" + (i32.const 0) if $"\t" else $"\09" end $"\u{09}" + (i32.const 0) if $" " else $"\ef\98\9a\ef\92\a9 " end $"\u{f61a}\u{f4a9} " + ) +) + +(assert_malformed (module quote "(func $)") "empty identifier") +(assert_malformed (module quote "(func $\"\")") "empty identifier") +(assert_malformed (module quote "(func $ \"a\")") "empty identifier") +(assert_malformed (module quote "(func $\"a\nb\")") "empty identifier") +(assert_malformed (module quote "(func $\"a\tb\")") "empty identifier") +(assert_malformed (module quote "(func $\"\\ef\")") "malformed UTF-8") diff --git a/test/core/imports.wast b/test/core/imports.wast index cbb41af8c..e65e5a276 100644 --- a/test/core/imports.wast +++ b/test/core/imports.wast @@ -13,9 +13,12 @@ (global (export "global-mut-i64") (mut i64) (i64.const 66)) (table (export "table-10-inf") 10 funcref) (table (export "table-10-20") 10 20 funcref) + (table (export "table64-10-inf") i64 10 funcref) + (table (export "table64-10-20") i64 10 20 funcref) (memory (export "memory-2-inf") 2) - ;; Multiple memories are not yet supported - ;; (memory (export "memory-2-4") 2 4) + (memory (export "memory-2-4") 2 4) + (memory (export "memory64-2-inf") i64 2) + (memory (export "memory64-2-4") i64 2 4) (tag (export "tag")) (tag $tag-i32 (param i32)) (export "tag-i32" (tag $tag-i32)) @@ -376,6 +379,7 @@ (module (type (func (result i32))) (import "spectest" "table" (table $tab 10 20 funcref)) + (import "test" "table64-10-inf" (table $tab64 i64 10 funcref)) (elem (table $tab) (i32.const 1) func $f $g) (func (export "call") (param i32) (result i32) @@ -395,6 +399,7 @@ (module (type (func (result i32))) (table $tab (import "spectest" "table") 10 20 funcref) + (table $tab64 (import "test" "table64-10-inf") i64 10 funcref) (elem (table $tab) (i32.const 1) func $f $g) (func (export "call") (param i32) (result i32) @@ -410,12 +415,15 @@ (assert_trap (invoke "call" (i32.const 3)) "uninitialized element") (assert_trap (invoke "call" (i32.const 100)) "undefined element") - (module (import "spectest" "table" (table 0 funcref)) (import "spectest" "table" (table 0 funcref)) + (import "test" "table64-10-inf" (table i64 10 funcref)) + (import "test" "table64-10-inf" (table i64 10 funcref)) (table 10 funcref) (table 10 funcref) + (table i64 10 funcref) + (table i64 10 funcref) ) (module (import "test" "table-10-inf" (table 10 funcref))) @@ -430,6 +438,18 @@ (module (import "test" "table-10-20" (table 10 25 funcref))) (module (import "test" "table-10-20" (table 5 25 funcref))) (module (import "test" "table-10-20" (table 0 25 funcref))) +(module (import "test" "table64-10-inf" (table i64 10 funcref))) +(module (import "test" "table64-10-inf" (table i64 5 funcref))) +(module (import "test" "table64-10-inf" (table i64 0 funcref))) +(module (import "test" "table64-10-20" (table i64 10 funcref))) +(module (import "test" "table64-10-20" (table i64 5 funcref))) +(module (import "test" "table64-10-20" (table i64 0 funcref))) +(module (import "test" "table64-10-20" (table i64 10 20 funcref))) +(module (import "test" "table64-10-20" (table i64 5 20 funcref))) +(module (import "test" "table64-10-20" (table i64 0 20 funcref))) +(module (import "test" "table64-10-20" (table i64 10 25 funcref))) +(module (import "test" "table64-10-20" (table i64 5 25 funcref))) +(module (import "test" "table64-10-20" (table i64 0 25 funcref))) (module (import "spectest" "table" (table 10 funcref))) (module (import "spectest" "table" (table 5 funcref))) (module (import "spectest" "table" (table 0 funcref))) @@ -456,6 +476,14 @@ (module (import "test" "table-10-inf" (table 10 20 funcref))) "incompatible import type" ) +(assert_unlinkable + (module (import "test" "table64-10-inf" (table i64 12 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table64-10-inf" (table i64 10 20 funcref))) + "incompatible import type" +) (assert_unlinkable (module (import "test" "table-10-20" (table 12 20 funcref))) "incompatible import type" @@ -464,6 +492,14 @@ (module (import "test" "table-10-20" (table 10 18 funcref))) "incompatible import type" ) +(assert_unlinkable + (module (import "test" "table64-10-20" (table i64 12 20 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table64-10-20" (table i64 10 18 funcref))) + "incompatible import type" +) (assert_unlinkable (module (import "spectest" "table" (table 12 funcref))) "incompatible import type" @@ -490,12 +526,30 @@ "incompatible import type" ) +(assert_unlinkable + (module (import "test" "table-10-inf" (table i64 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table64-10-inf" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-20" (table i64 10 20 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table64-10-20" (table 10 20 funcref))) + "incompatible import type" +) ;; Memories (module (import "spectest" "memory" (memory 1 2)) + (import "test" "memory-2-inf" (memory 2)) + (import "test" "memory64-2-inf" (memory i64 2)) (data (memory 0) (i32.const 10) "\10") (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) @@ -508,6 +562,8 @@ (module (memory (import "spectest" "memory") 1 2) + (memory (import "test" "memory-2-inf") 2) + (memory (import "test" "memory64-2-inf") i64 2) (data (memory 0) (i32.const 10) "\10") (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) @@ -520,6 +576,26 @@ (module (import "test" "memory-2-inf" (memory 2))) (module (import "test" "memory-2-inf" (memory 1))) (module (import "test" "memory-2-inf" (memory 0))) +(module (import "test" "memory-2-4" (memory 2))) +(module (import "test" "memory-2-4" (memory 1))) +(module (import "test" "memory-2-4" (memory 0))) +(module (import "test" "memory-2-4" (memory 2 4))) +(module (import "test" "memory-2-4" (memory 1 4))) +(module (import "test" "memory-2-4" (memory 0 4))) +(module (import "test" "memory-2-4" (memory 2 5))) +(module (import "test" "memory-2-4" (memory 2 6))) +(module (import "test" "memory64-2-inf" (memory i64 2))) +(module (import "test" "memory64-2-inf" (memory i64 1))) +(module (import "test" "memory64-2-inf" (memory i64 0))) +(module (import "test" "memory64-2-4" (memory i64 2))) +(module (import "test" "memory64-2-4" (memory i64 1))) +(module (import "test" "memory64-2-4" (memory i64 0))) +(module (import "test" "memory64-2-4" (memory i64 2 4))) +(module (import "test" "memory64-2-4" (memory i64 1 4))) +(module (import "test" "memory64-2-4" (memory i64 0 4))) +(module (import "test" "memory64-2-4" (memory i64 2 5))) +(module (import "test" "memory64-2-4" (memory i64 1 5))) +(module (import "test" "memory64-2-4" (memory i64 0 5))) (module (import "spectest" "memory" (memory 1))) (module (import "spectest" "memory" (memory 0))) (module (import "spectest" "memory" (memory 1 2))) @@ -537,13 +613,149 @@ ) (assert_unlinkable - (module (import "test" "memory-2-inf" (memory 3))) + (module (import "test" "memory-2-inf" (memory 0 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory 0 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory 0 3))) "incompatible import type" ) (assert_unlinkable (module (import "test" "memory-2-inf" (memory 2 3))) "incompatible import type" ) +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 0 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 0 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 0 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 2 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 2 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 3 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 3 4))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 3 5))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 4 4))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 4 5))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 4))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory 5))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-inf" (memory i64 0 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-inf" (memory i64 0 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-inf" (memory i64 0 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-inf" (memory i64 2 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-inf" (memory i64 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 0 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 0 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 0 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 2 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 2 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 3 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 3 4))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 3 5))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 4 4))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 4 5))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 4))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory i64 5))) + "incompatible import type" +) (assert_unlinkable (module (import "spectest" "memory" (memory 2))) "incompatible import type" @@ -553,6 +765,23 @@ "incompatible import type" ) +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory i64 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-inf" (memory 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-4" (memory i64 2 4))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory64-2-4" (memory 2 4))) + "incompatible import type" +) + (assert_unlinkable (module (import "test" "func-i32" (memory 1))) "incompatible import type" @@ -597,26 +826,6 @@ (assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) (assert_return (invoke "grow" (i32.const 0)) (i32.const 2)) -(module $Mgm - (memory (export "memory") 1) ;; initial size is 1 - (func (export "grow") (result i32) (memory.grow (i32.const 1))) -) -(register "grown-memory" $Mgm) -(assert_return (invoke $Mgm "grow") (i32.const 1)) ;; now size is 2 -(module $Mgim1 - ;; imported memory limits should match, because external memory size is 2 now - (memory (export "memory") (import "grown-memory" "memory") 2) - (func (export "grow") (result i32) (memory.grow (i32.const 1))) -) -(register "grown-imported-memory" $Mgim1) -(assert_return (invoke $Mgim1 "grow") (i32.const 2)) ;; now size is 3 -(module $Mgim2 - ;; imported memory limits should match, because external memory size is 3 now - (import "grown-imported-memory" "memory" (memory 3)) - (func (export "size") (result i32) (memory.size)) -) -(assert_return (invoke $Mgim2 "size") (i32.const 3)) - ;; Syntax errors diff --git a/test/core/instance.wast b/test/core/instance.wast new file mode 100644 index 000000000..b88025d69 --- /dev/null +++ b/test/core/instance.wast @@ -0,0 +1,170 @@ +;; Instantiation is generative + +(module definition $M + (global (export "glob") (mut i32) (i32.const 0)) + (table (export "tab") 10 funcref (ref.null func)) + (memory (export "mem") 1) + (tag (export "tag")) +) + +(module instance $I1 $M) +(module instance $I2 $M) +(register "I1" $I1) +(register "I2" $I2) + +(module + (import "I1" "glob" (global $glob1 (mut i32))) + (import "I2" "glob" (global $glob2 (mut i32))) + (import "I1" "tab" (table $tab1 10 funcref)) + (import "I2" "tab" (table $tab2 10 funcref)) + (import "I1" "mem" (memory $mem1 1)) + (import "I2" "mem" (memory $mem2 1)) + (import "I1" "tag" (tag $tag1)) + (import "I2" "tag" (tag $tag2)) + + (func $f) + (elem declare func $f) + + (func (export "glob") (result i32) + (global.set $glob1 (i32.const 1)) + (global.get $glob2) + ) + (func (export "tab") (result funcref) + (table.set $tab1 (i32.const 0) (ref.func $f)) + (table.get $tab2 (i32.const 0)) + ) + (func (export "mem") (result i32) + (i32.store $mem1 (i32.const 0) (i32.const 1)) + (i32.load $mem2 (i32.const 0)) + ) + (func (export "tag") (result i32) + (block $on_tag1 + (block $on_other + (try_table (catch $tag1 $on_tag1) (catch_all $on_other) + (throw $tag2) + ) + (unreachable) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) +) + +(assert_return (invoke "glob") (i32.const 0)) +(assert_return (invoke "tab") (ref.null)) +(assert_return (invoke "mem") (i32.const 0)) +(assert_return (invoke "tag") (i32.const 0)) + + +;; Import is not generative + +(module + (import "I1" "glob" (global $glob1 (mut i32))) + (import "I1" "glob" (global $glob2 (mut i32))) + (import "I1" "tab" (table $tab1 10 funcref)) + (import "I1" "tab" (table $tab2 10 funcref)) + (import "I1" "mem" (memory $mem1 1)) + (import "I1" "mem" (memory $mem2 1)) + (import "I1" "tag" (tag $tag1)) + (import "I1" "tag" (tag $tag2)) + + (func $f) + (elem declare func $f) + + (func (export "glob") (result i32) + (global.set $glob1 (i32.const 1)) + (global.get $glob2) + ) + (func (export "tab") (result funcref) + (table.set $tab1 (i32.const 0) (ref.func $f)) + (table.get $tab2 (i32.const 0)) + ) + (func (export "mem") (result i32) + (i32.store $mem1 (i32.const 0) (i32.const 1)) + (i32.load $mem2 (i32.const 0)) + ) + (func (export "tag") (result i32) + (block $on_tag1 + (block $on_other + (try_table (catch $tag1 $on_tag1) (catch_all $on_other) + (throw $tag2) + ) + (unreachable) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) +) + +(assert_return (invoke "glob") (i32.const 1)) +(assert_return (invoke "tab") (ref.func)) +(assert_return (invoke "mem") (i32.const 1)) +(assert_return (invoke "tag") (i32.const 1)) + + +;; Export is not generative + +(module definition $N + (global $glob (mut i32) (i32.const 0)) + (table $tab 10 funcref (ref.null func)) + (memory $mem 1) + (tag $tag) + + (export "glob1" (global $glob)) + (export "glob2" (global $glob)) + (export "tab1" (table $tab)) + (export "tab2" (table $tab)) + (export "mem1" (memory $mem)) + (export "mem2" (memory $mem)) + (export "tag1" (tag $tag)) + (export "tag2" (tag $tag)) +) + +(module instance $I $N) +(register "I" $I) + +(module + (import "I" "glob1" (global $glob1 (mut i32))) + (import "I" "glob2" (global $glob2 (mut i32))) + (import "I" "tab1" (table $tab1 10 funcref)) + (import "I" "tab2" (table $tab2 10 funcref)) + (import "I" "mem1" (memory $mem1 1)) + (import "I" "mem2" (memory $mem2 1)) + (import "I" "tag1" (tag $tag1)) + (import "I" "tag2" (tag $tag2)) + + (func $f) + (elem declare func $f) + + (func (export "glob") (result i32) + (global.set $glob1 (i32.const 1)) + (global.get $glob2) + ) + (func (export "tab") (result funcref) + (table.set $tab1 (i32.const 0) (ref.func $f)) + (table.get $tab2 (i32.const 0)) + ) + (func (export "mem") (result i32) + (i32.store $mem1 (i32.const 0) (i32.const 1)) + (i32.load $mem2 (i32.const 0)) + ) + (func (export "tag") (result i32) + (block $on_tag1 + (block $on_other + (try_table (catch $tag1 $on_tag1) (catch_all $on_other) + (throw $tag2) + ) + (unreachable) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) +) + +(assert_return (invoke "glob") (i32.const 1)) +(assert_return (invoke "tab") (ref.func)) +(assert_return (invoke "mem") (i32.const 1)) +(assert_return (invoke "tag") (i32.const 1)) diff --git a/test/core/load64.wast b/test/core/load64.wast new file mode 100644 index 000000000..e4a6a9833 --- /dev/null +++ b/test/core/load64.wast @@ -0,0 +1,567 @@ +;; Load operator as the argument of control constructs and instructions + +(module + (memory i64 1) + + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (i32.load (i64.const 0)))) + ) + + (func (export "as-br_if-cond") + (block (br_if 0 (i32.load (i64.const 0)))) + ) + (func (export "as-br_if-value") (result i32) + (block (result i32) + (drop (br_if 0 (i32.load (i64.const 0)) (i32.const 1))) (i32.const 7) + ) + ) + (func (export "as-br_if-value-cond") (result i32) + (block (result i32) + (drop (br_if 0 (i32.const 6) (i32.load (i64.const 0)))) (i32.const 7) + ) + ) + + (func (export "as-br_table-index") + (block (br_table 0 0 0 (i32.load (i64.const 0)))) + ) + (func (export "as-br_table-value") (result i32) + (block (result i32) + (br_table 0 0 0 (i32.load (i64.const 0)) (i32.const 1)) (i32.const 7) + ) + ) + (func (export "as-br_table-value-index") (result i32) + (block (result i32) + (br_table 0 0 (i32.const 6) (i32.load (i64.const 0))) (i32.const 7) + ) + ) + + (func (export "as-return-value") (result i32) + (return (i32.load (i64.const 0))) + ) + + (func (export "as-if-cond") (result i32) + (if (result i32) (i32.load (i64.const 0)) + (then (i32.const 0)) (else (i32.const 1)) + ) + ) + (func (export "as-if-then") (result i32) + (if (result i32) (i32.const 1) + (then (i32.load (i64.const 0))) (else (i32.const 0)) + ) + ) + (func (export "as-if-else") (result i32) + (if (result i32) (i32.const 0) + (then (i32.const 0)) (else (i32.load (i64.const 0))) + ) + ) + + (func (export "as-select-first") (param i32 i32) (result i32) + (select (i32.load (i64.const 0)) (local.get 0) (local.get 1)) + ) + (func (export "as-select-second") (param i32 i32) (result i32) + (select (local.get 0) (i32.load (i64.const 0)) (local.get 1)) + ) + (func (export "as-select-cond") (result i32) + (select (i32.const 0) (i32.const 1) (i32.load (i64.const 0))) + ) + + (func $f (param i32 i32 i32) (result i32) (i32.const -1)) + (func (export "as-call-first") (result i32) + (call $f (i32.load (i64.const 0)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-call-mid") (result i32) + (call $f (i32.const 1) (i32.load (i64.const 0)) (i32.const 3)) + ) + (func (export "as-call-last") (result i32) + (call $f (i32.const 1) (i32.const 2) (i32.load (i64.const 0))) + ) + + (type $sig (func (param i32 i32 i32) (result i32))) + (table funcref (elem $f)) + (func (export "as-call_indirect-first") (result i32) + (call_indirect (type $sig) + (i32.load (i64.const 0)) (i32.const 2) (i32.const 3) (i32.const 0) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (call_indirect (type $sig) + (i32.const 1) (i32.load (i64.const 0)) (i32.const 3) (i32.const 0) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (call_indirect (type $sig) + (i32.const 1) (i32.const 2) (i32.load (i64.const 0)) (i32.const 0) + ) + ) + (func (export "as-call_indirect-index") (result i32) + (call_indirect (type $sig) + (i32.const 1) (i32.const 2) (i32.const 3) (i32.load (i64.const 0)) + ) + ) + + (func (export "as-local.set-value") (local i32) + (local.set 0 (i32.load (i64.const 0))) + ) + (func (export "as-local.tee-value") (result i32) (local i32) + (local.tee 0 (i32.load (i64.const 0))) + ) + (global $g (mut i32) (i32.const 0)) + (func (export "as-global.set-value") (local i32) + (global.set $g (i32.load (i64.const 0))) + ) + + (func (export "as-load-address") (result i32) + (i32.load (i64.load (i64.const 0))) + ) + (func (export "as-loadN-address") (result i32) + (i32.load8_s (i64.load (i64.const 0))) + ) + + (func (export "as-store-address") + (i32.store (i64.load (i64.const 0)) (i32.const 7)) + ) + (func (export "as-store-value") + (i32.store (i64.const 2) (i32.load (i64.const 0))) + ) + + (func (export "as-storeN-address") + (i32.store8 (i64.load8_s (i64.const 0)) (i32.const 7)) + ) + (func (export "as-storeN-value") + (i32.store16 (i64.const 2) (i32.load (i64.const 0))) + ) + + (func (export "as-unary-operand") (result i32) + (i32.clz (i32.load (i64.const 100))) + ) + + (func (export "as-binary-left") (result i32) + (i32.add (i32.load (i64.const 100)) (i32.const 10)) + ) + (func (export "as-binary-right") (result i32) + (i32.sub (i32.const 10) (i32.load (i64.const 100))) + ) + + (func (export "as-test-operand") (result i32) + (i32.eqz (i32.load (i64.const 100))) + ) + + (func (export "as-compare-left") (result i32) + (i32.le_s (i32.load (i64.const 100)) (i32.const 10)) + ) + (func (export "as-compare-right") (result i32) + (i32.ne (i32.const 10) (i32.load (i64.const 100))) + ) + + (func (export "as-memory.grow-size") (result i64) + (memory.grow (i64.load (i64.const 100))) + ) +) + +(assert_return (invoke "as-br-value") (i32.const 0)) + +(assert_return (invoke "as-br_if-cond")) +(assert_return (invoke "as-br_if-value") (i32.const 0)) +(assert_return (invoke "as-br_if-value-cond") (i32.const 7)) + +(assert_return (invoke "as-br_table-index")) +(assert_return (invoke "as-br_table-value") (i32.const 0)) +(assert_return (invoke "as-br_table-value-index") (i32.const 6)) + +(assert_return (invoke "as-return-value") (i32.const 0)) + +(assert_return (invoke "as-if-cond") (i32.const 1)) +(assert_return (invoke "as-if-then") (i32.const 0)) +(assert_return (invoke "as-if-else") (i32.const 0)) + +(assert_return (invoke "as-select-first" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "as-select-second" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-select-cond") (i32.const 1)) + +(assert_return (invoke "as-call-first") (i32.const -1)) +(assert_return (invoke "as-call-mid") (i32.const -1)) +(assert_return (invoke "as-call-last") (i32.const -1)) + +(assert_return (invoke "as-call_indirect-first") (i32.const -1)) +(assert_return (invoke "as-call_indirect-mid") (i32.const -1)) +(assert_return (invoke "as-call_indirect-last") (i32.const -1)) +(assert_return (invoke "as-call_indirect-index") (i32.const -1)) + +(assert_return (invoke "as-local.set-value")) +(assert_return (invoke "as-local.tee-value") (i32.const 0)) +(assert_return (invoke "as-global.set-value")) + +(assert_return (invoke "as-load-address") (i32.const 0)) +(assert_return (invoke "as-loadN-address") (i32.const 0)) +(assert_return (invoke "as-store-address")) +(assert_return (invoke "as-store-value")) +(assert_return (invoke "as-storeN-address")) +(assert_return (invoke "as-storeN-value")) + +(assert_return (invoke "as-unary-operand") (i32.const 32)) + +(assert_return (invoke "as-binary-left") (i32.const 10)) +(assert_return (invoke "as-binary-right") (i32.const 10)) + +(assert_return (invoke "as-test-operand") (i32.const 1)) + +(assert_return (invoke "as-compare-left") (i32.const 1)) +(assert_return (invoke "as-compare-right") (i32.const 1)) + +(assert_return (invoke "as-memory.grow-size") (i64.const 1)) + +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result i32) (i32.load32 (local.get 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result i32) (i32.load32_u (local.get 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result i32) (i32.load32_s (local.get 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result i32) (i32.load64 (local.get 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result i32) (i32.load64_u (local.get 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result i32) (i32.load64_s (local.get 0)))" + ) + "unknown operator" +) + +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result i64) (i64.load64 (local.get 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result i64) (i64.load64_u (local.get 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result i64) (i64.load64_s (local.get 0)))" + ) + "unknown operator" +) + +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result f32) (f32.load32 (local.get 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result f32) (f32.load64 (local.get 0)))" + ) + "unknown operator" +) + +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result f64) (f64.load32 (local.get 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory i64 1)" + "(func (param i64) (result f64) (f64.load64 (local.get 0)))" + ) + "unknown operator" +) + + +;; load should have retval + +(assert_invalid + (module (memory i64 1) (func $load_i32 (i32.load (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load8_s_i32 (i32.load8_s (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load8_u_i32 (i32.load8_u (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load16_s_i32 (i32.load16_s (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load16_u_i32 (i32.load16_u (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load_i64 (i64.load (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load8_s_i64 (i64.load8_s (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load8_u_i64 (i64.load8_u (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load16_s_i64 (i64.load16_s (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load16_u_i64 (i64.load16_u (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load32_s_i64 (i64.load32_s (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load32_u_i64 (i64.load32_u (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load_f32 (f32.load (i64.const 0)))) + "type mismatch" +) +(assert_invalid + (module (memory i64 1) (func $load_f64 (f64.load (i64.const 0)))) + "type mismatch" +) + + +;; Type check + +(assert_invalid (module (memory i64 1) (func (result i32) (i32.load (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result i32) (i32.load8_s (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result i32) (i32.load8_u (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result i32) (i32.load16_s (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result i32) (i32.load16_u (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result i64) (i64.load (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result i64) (i64.load8_s (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result i64) (i64.load8_u (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result i64) (i64.load16_s (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result i64) (i64.load16_u (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result i64) (i64.load32_s (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result i64) (i64.load32_u (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result f32) (f32.load (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory i64 1) (func (result f64) (f64.load (f32.const 0)))) "type mismatch") + + +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty + (i32.load) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-block + (i32.const 0) + (block (i32.load) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-loop + (i32.const 0) + (loop (i32.load) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (i32.load) (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-else + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (i32.load))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-br + (i32.const 0) + (block (br 0 (i32.load)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-br_if + (i32.const 0) + (block (br_if 0 (i32.load) (i32.const 1)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-br_table + (i32.const 0) + (block (br_table 0 (i32.load)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-return + (return (i32.load)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-select + (select (i32.load) (i32.const 1) (i32.const 2)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-call + (call 1 (i32.load)) (drop) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-address-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (i32.load) (i32.const 0) + ) + (drop) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-local.set + (local i32) + (local.set 0 (i32.load)) (local.get 0) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-local.tee + (local i32) + (local.tee 0 (i32.load)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (global $x (mut i32) (i32.const 0)) + (func $type-address-empty-in-global.set + (global.set $x (i32.load)) (global.get $x) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-memory.grow + (memory.grow (i64.load)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 0) + (func $type-address-empty-in-load + (i32.load (i32.load)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory i64 1) + (func $type-address-empty-in-store + (i32.store (i32.load) (i32.const 1)) + ) + ) + "type mismatch" +) diff --git a/test/core/memory.wast b/test/core/memory.wast index 56ffd6b52..e01170a8f 100644 --- a/test/core/memory.wast +++ b/test/core/memory.wast @@ -50,40 +50,53 @@ ) (assert_invalid (module (memory 65537)) - "memory size must be at most 65536 pages (4GiB)" + "memory size" ) (assert_invalid (module (memory 2147483648)) - "memory size must be at most 65536 pages (4GiB)" + "memory size" ) (assert_invalid (module (memory 4294967295)) - "memory size must be at most 65536 pages (4GiB)" + "memory size" ) (assert_invalid (module (memory 0 65537)) - "memory size must be at most 65536 pages (4GiB)" + "memory size" ) (assert_invalid (module (memory 0 2147483648)) - "memory size must be at most 65536 pages (4GiB)" + "memory size" ) (assert_invalid (module (memory 0 4294967295)) - "memory size must be at most 65536 pages (4GiB)" + "memory size" ) -(assert_malformed - (module quote "(memory 0x1_0000_0000)") - "i32 constant out of range" +(assert_invalid + (module (memory 0x1_0000_0000)) + "memory size" +) +(assert_invalid + (module (memory 0x1_0000_0000 0x1_0000_0000)) + "memory size" +) +(assert_invalid + (module (memory 0 0x1_0000_0000)) + "memory size" ) -(assert_malformed - (module quote "(memory 0x1_0000_0000 0x1_0000_0000)") - "i32 constant out of range" + +(assert_invalid + (module (memory (import "M" "m") 0x1_0000_0000)) + "memory size" +) +(assert_invalid + (module (memory (import "M" "m") 0x1_0000_0000 0x1_0000_0000)) + "memory size" ) -(assert_malformed - (module quote "(memory 0 0x1_0000_0000)") - "i32 constant out of range" +(assert_invalid + (module (memory (import "M" "m") 0 0x1_0000_0000)) + "memory size" ) (module @@ -133,44 +146,44 @@ ;; Sign and zero extending memory loads (func (export "i32_load8_s") (param $i i32) (result i32) - (i32.store8 (i32.const 8) (local.get $i)) - (i32.load8_s (i32.const 8)) + (i32.store8 (i32.const 8) (local.get $i)) + (i32.load8_s (i32.const 8)) ) (func (export "i32_load8_u") (param $i i32) (result i32) - (i32.store8 (i32.const 8) (local.get $i)) - (i32.load8_u (i32.const 8)) + (i32.store8 (i32.const 8) (local.get $i)) + (i32.load8_u (i32.const 8)) ) (func (export "i32_load16_s") (param $i i32) (result i32) - (i32.store16 (i32.const 8) (local.get $i)) - (i32.load16_s (i32.const 8)) + (i32.store16 (i32.const 8) (local.get $i)) + (i32.load16_s (i32.const 8)) ) (func (export "i32_load16_u") (param $i i32) (result i32) - (i32.store16 (i32.const 8) (local.get $i)) - (i32.load16_u (i32.const 8)) + (i32.store16 (i32.const 8) (local.get $i)) + (i32.load16_u (i32.const 8)) ) (func (export "i64_load8_s") (param $i i64) (result i64) - (i64.store8 (i32.const 8) (local.get $i)) - (i64.load8_s (i32.const 8)) + (i64.store8 (i32.const 8) (local.get $i)) + (i64.load8_s (i32.const 8)) ) (func (export "i64_load8_u") (param $i i64) (result i64) - (i64.store8 (i32.const 8) (local.get $i)) - (i64.load8_u (i32.const 8)) + (i64.store8 (i32.const 8) (local.get $i)) + (i64.load8_u (i32.const 8)) ) (func (export "i64_load16_s") (param $i i64) (result i64) - (i64.store16 (i32.const 8) (local.get $i)) - (i64.load16_s (i32.const 8)) + (i64.store16 (i32.const 8) (local.get $i)) + (i64.load16_s (i32.const 8)) ) (func (export "i64_load16_u") (param $i i64) (result i64) - (i64.store16 (i32.const 8) (local.get $i)) - (i64.load16_u (i32.const 8)) + (i64.store16 (i32.const 8) (local.get $i)) + (i64.load16_u (i32.const 8)) ) (func (export "i64_load32_s") (param $i i64) (result i64) - (i64.store32 (i32.const 8) (local.get $i)) - (i64.load32_s (i32.const 8)) + (i64.store32 (i32.const 8) (local.get $i)) + (i64.load32_s (i32.const 8)) ) (func (export "i64_load32_u") (param $i i64) (result i64) - (i64.store32 (i32.const 8) (local.get $i)) - (i64.load32_u (i32.const 8)) + (i64.store32 (i32.const 8) (local.get $i)) + (i64.load32_u (i32.const 8)) ) ) @@ -237,3 +250,28 @@ "(import \"\" \"\" (memory $foo 1))" "(import \"\" \"\" (memory $foo 1))") "duplicate memory") + +;; Test that exporting random globals does not change a memory's semantics. + +(module + (memory (export "memory") 1 1) + + ;; These should not change the behavior of memory accesses. + (global (export "__data_end") i32 (i32.const 10000)) + (global (export "__stack_top") i32 (i32.const 10000)) + (global (export "__heap_base") i32 (i32.const 10000)) + + (func (export "load") (param i32) (result i32) + (i32.load8_u (local.get 0)) + ) +) + +;; None of these memory accesses should trap. +(assert_return (invoke "load" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 10000)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 20000)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 30000)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 40000)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 50000)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 60000)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 65535)) (i32.const 0)) diff --git a/test/core/memory64.wast b/test/core/memory64.wast new file mode 100644 index 000000000..ba3b24233 --- /dev/null +++ b/test/core/memory64.wast @@ -0,0 +1,204 @@ +;; Test memory section structure +;; Largely duplicated from memory, but with all memories using a 64-bit index. + +(module (memory i64 0 0)) +(module (memory i64 0 1)) +(module (memory i64 1 256)) +(module (memory i64 0 65536)) + +(module (memory i64 (data)) (func (export "memsize") (result i64) (memory.size))) +(assert_return (invoke "memsize") (i64.const 0)) +(module (memory i64 (data "")) (func (export "memsize") (result i64) (memory.size))) +(assert_return (invoke "memsize") (i64.const 0)) +(module (memory i64 (data "x")) (func (export "memsize") (result i64) (memory.size))) +(assert_return (invoke "memsize") (i64.const 1)) + +(assert_invalid (module (data (i64.const 0))) "unknown memory") +(assert_invalid (module (data (i64.const 0) "")) "unknown memory") +(assert_invalid (module (data (i64.const 0) "x")) "unknown memory") + +(assert_invalid + (module (func (drop (f32.load (i64.const 0))))) + "unknown memory" +) +(assert_invalid + (module (func (f32.store (i64.const 0) (f32.const 0)))) + "unknown memory" +) +(assert_invalid + (module (func (drop (i32.load8_s (i64.const 0))))) + "unknown memory" +) +(assert_invalid + (module (func (i32.store8 (i64.const 0) (i32.const 0)))) + "unknown memory" +) +(assert_invalid + (module (func (drop (memory.size)))) + "unknown memory" +) +(assert_invalid + (module (func (drop (memory.grow (i64.const 0))))) + "unknown memory" +) + + +(assert_invalid + (module (memory i64 1 0)) + "size minimum must not be greater than maximum" +) + +(assert_invalid + (module (memory i64 0x1_0000_0000_0001)) + "memory size" +) +(assert_invalid + (module (memory i64 0 0x1_0000_0000_0001)) + "memory size" +) + +(assert_invalid + (module (memory (import "M" "m") i64 0x1_0000_0000_0001)) + "memory size" +) +(assert_invalid + (module (memory (import "M" "m") i64 0 0x1_0000_0000_0001)) + "memory size" +) + +(module + (memory i64 1) + (data (i64.const 0) "ABC\a7D") (data (i64.const 20) "WASM") + + ;; Data section + (func (export "data") (result i32) + (i32.and + (i32.and + (i32.and + (i32.eq (i32.load8_u (i64.const 0)) (i32.const 65)) + (i32.eq (i32.load8_u (i64.const 3)) (i32.const 167)) + ) + (i32.and + (i32.eq (i32.load8_u (i64.const 6)) (i32.const 0)) + (i32.eq (i32.load8_u (i64.const 19)) (i32.const 0)) + ) + ) + (i32.and + (i32.and + (i32.eq (i32.load8_u (i64.const 20)) (i32.const 87)) + (i32.eq (i32.load8_u (i64.const 23)) (i32.const 77)) + ) + (i32.and + (i32.eq (i32.load8_u (i64.const 24)) (i32.const 0)) + (i32.eq (i32.load8_u (i64.const 1023)) (i32.const 0)) + ) + ) + ) + ) + + ;; Memory cast + (func (export "cast") (result f64) + (i64.store (i64.const 8) (i64.const -12345)) + (if + (f64.eq + (f64.load (i64.const 8)) + (f64.reinterpret_i64 (i64.const -12345)) + ) + (then (return (f64.const 0))) + ) + (i64.store align=1 (i64.const 9) (i64.const 0)) + (i32.store16 align=1 (i64.const 15) (i32.const 16453)) + (f64.load align=1 (i64.const 9)) + ) + + ;; Sign and zero extending memory loads + (func (export "i32_load8_s") (param $i i32) (result i32) + (i32.store8 (i64.const 8) (local.get $i)) + (i32.load8_s (i64.const 8)) + ) + (func (export "i32_load8_u") (param $i i32) (result i32) + (i32.store8 (i64.const 8) (local.get $i)) + (i32.load8_u (i64.const 8)) + ) + (func (export "i32_load16_s") (param $i i32) (result i32) + (i32.store16 (i64.const 8) (local.get $i)) + (i32.load16_s (i64.const 8)) + ) + (func (export "i32_load16_u") (param $i i32) (result i32) + (i32.store16 (i64.const 8) (local.get $i)) + (i32.load16_u (i64.const 8)) + ) + (func (export "i64_load8_s") (param $i i64) (result i64) + (i64.store8 (i64.const 8) (local.get $i)) + (i64.load8_s (i64.const 8)) + ) + (func (export "i64_load8_u") (param $i i64) (result i64) + (i64.store8 (i64.const 8) (local.get $i)) + (i64.load8_u (i64.const 8)) + ) + (func (export "i64_load16_s") (param $i i64) (result i64) + (i64.store16 (i64.const 8) (local.get $i)) + (i64.load16_s (i64.const 8)) + ) + (func (export "i64_load16_u") (param $i i64) (result i64) + (i64.store16 (i64.const 8) (local.get $i)) + (i64.load16_u (i64.const 8)) + ) + (func (export "i64_load32_s") (param $i i64) (result i64) + (i64.store32 (i64.const 8) (local.get $i)) + (i64.load32_s (i64.const 8)) + ) + (func (export "i64_load32_u") (param $i i64) (result i64) + (i64.store32 (i64.const 8) (local.get $i)) + (i64.load32_u (i64.const 8)) + ) +) + +(assert_return (invoke "data") (i32.const 1)) +(assert_return (invoke "cast") (f64.const 42.0)) + +(assert_return (invoke "i32_load8_s" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "i32_load8_u" (i32.const -1)) (i32.const 255)) +(assert_return (invoke "i32_load16_s" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "i32_load16_u" (i32.const -1)) (i32.const 65535)) + +(assert_return (invoke "i32_load8_s" (i32.const 100)) (i32.const 100)) +(assert_return (invoke "i32_load8_u" (i32.const 200)) (i32.const 200)) +(assert_return (invoke "i32_load16_s" (i32.const 20000)) (i32.const 20000)) +(assert_return (invoke "i32_load16_u" (i32.const 40000)) (i32.const 40000)) + +(assert_return (invoke "i32_load8_s" (i32.const 0xfedc6543)) (i32.const 0x43)) +(assert_return (invoke "i32_load8_s" (i32.const 0x3456cdef)) (i32.const 0xffffffef)) +(assert_return (invoke "i32_load8_u" (i32.const 0xfedc6543)) (i32.const 0x43)) +(assert_return (invoke "i32_load8_u" (i32.const 0x3456cdef)) (i32.const 0xef)) +(assert_return (invoke "i32_load16_s" (i32.const 0xfedc6543)) (i32.const 0x6543)) +(assert_return (invoke "i32_load16_s" (i32.const 0x3456cdef)) (i32.const 0xffffcdef)) +(assert_return (invoke "i32_load16_u" (i32.const 0xfedc6543)) (i32.const 0x6543)) +(assert_return (invoke "i32_load16_u" (i32.const 0x3456cdef)) (i32.const 0xcdef)) + +(assert_return (invoke "i64_load8_s" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load8_u" (i64.const -1)) (i64.const 255)) +(assert_return (invoke "i64_load16_s" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load16_u" (i64.const -1)) (i64.const 65535)) +(assert_return (invoke "i64_load32_s" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load32_u" (i64.const -1)) (i64.const 4294967295)) + +(assert_return (invoke "i64_load8_s" (i64.const 100)) (i64.const 100)) +(assert_return (invoke "i64_load8_u" (i64.const 200)) (i64.const 200)) +(assert_return (invoke "i64_load16_s" (i64.const 20000)) (i64.const 20000)) +(assert_return (invoke "i64_load16_u" (i64.const 40000)) (i64.const 40000)) +(assert_return (invoke "i64_load32_s" (i64.const 20000)) (i64.const 20000)) +(assert_return (invoke "i64_load32_u" (i64.const 40000)) (i64.const 40000)) + +(assert_return (invoke "i64_load8_s" (i64.const 0xfedcba9856346543)) (i64.const 0x43)) +(assert_return (invoke "i64_load8_s" (i64.const 0x3456436598bacdef)) (i64.const 0xffffffffffffffef)) +(assert_return (invoke "i64_load8_u" (i64.const 0xfedcba9856346543)) (i64.const 0x43)) +(assert_return (invoke "i64_load8_u" (i64.const 0x3456436598bacdef)) (i64.const 0xef)) +(assert_return (invoke "i64_load16_s" (i64.const 0xfedcba9856346543)) (i64.const 0x6543)) +(assert_return (invoke "i64_load16_s" (i64.const 0x3456436598bacdef)) (i64.const 0xffffffffffffcdef)) +(assert_return (invoke "i64_load16_u" (i64.const 0xfedcba9856346543)) (i64.const 0x6543)) +(assert_return (invoke "i64_load16_u" (i64.const 0x3456436598bacdef)) (i64.const 0xcdef)) +(assert_return (invoke "i64_load32_s" (i64.const 0xfedcba9856346543)) (i64.const 0x56346543)) +(assert_return (invoke "i64_load32_s" (i64.const 0x3456436598bacdef)) (i64.const 0xffffffff98bacdef)) +(assert_return (invoke "i64_load32_u" (i64.const 0xfedcba9856346543)) (i64.const 0x56346543)) +(assert_return (invoke "i64_load32_u" (i64.const 0x3456436598bacdef)) (i64.const 0x98bacdef)) diff --git a/test/core/memory_copy.wast b/test/core/memory_copy.wast index 472995d79..680bba5a8 100644 --- a/test/core/memory_copy.wast +++ b/test/core/memory_copy.wast @@ -5576,3 +5576,5577 @@ (i32.const -1)) (assert_return (invoke "checkRange" (i32.const 64834) (i32.const 65536) (i32.const 0)) (i32.const -1)) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data (i64.const 12) "\07\05\02\03\06") + (func (export "test") + (nop)) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data (i64.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i64.const 13) (i64.const 2) (i64.const 3))) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data (i64.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i64.const 25) (i64.const 15) (i64.const 2))) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 6)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data (i64.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i64.const 13) (i64.const 25) (i64.const 3))) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data (i64.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i64.const 20) (i64.const 22) (i64.const 4))) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data (i64.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i64.const 25) (i64.const 1) (i64.const 3))) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data (i64.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i64.const 10) (i64.const 12) (i64.const 7))) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 5)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 6)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data (i64.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i64.const 12) (i64.const 10) (i64.const 7))) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 5)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 6)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 0) (i32.const 40)) + "out of bounds memory access") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 218)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 417)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 616)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 815)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1014)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1213)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1412)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1611)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1810)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2009)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2208)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2407)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2606)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2805)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3004)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3203)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3402)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3601)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3800)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3999)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65490)) (i32.const 0)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65515) (i32.const 0) (i32.const 39)) + "out of bounds memory access") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 20)) +(assert_return (invoke "load8_u" (i32.const 219)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 418)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 617)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 816)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1015)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1214)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1413)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1612)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1811)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2010)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2209)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2408)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2607)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2806)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3005)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3204)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3403)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3602)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3801)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4000)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4199)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4398)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4597)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4796)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4995)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5194)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5393)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5592)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5791)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5990)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6189)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6388)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6587)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6786)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6985)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7184)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7383)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7582)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7781)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7980)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8179)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8378)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8577)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8776)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8975)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9174)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9373)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9572)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9771)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9970)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10169)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10368)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10567)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10766)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10965)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11164)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11363)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11562)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11761)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11960)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12159)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12358)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12557)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12756)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12955)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13154)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13353)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13552)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13751)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13950)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14149)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14348)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14547)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14746)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14945)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15144)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15343)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15542)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15741)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15940)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16139)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16338)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16537)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16736)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16935)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17134)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17333)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17532)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17731)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17930)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18129)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18328)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18527)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18726)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18925)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19124)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19323)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19522)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19721)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19920)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20119)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20318)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20517)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20716)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20915)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21114)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21313)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21512)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21711)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21910)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22109)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22308)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22507)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22706)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22905)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23104)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23303)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23502)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23701)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23900)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24099)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24298)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24497)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24696)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24895)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25094)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25293)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25492)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25691)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25890)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26089)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26288)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26487)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26686)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26885)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27084)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27283)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27482)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27681)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27880)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28079)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28278)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28477)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28676)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28875)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29074)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29273)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29472)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29671)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29870)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30069)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30268)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30467)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30666)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30865)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31064)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31263)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31462)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31661)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31860)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32059)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32258)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32457)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32656)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32855)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33054)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33253)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33452)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33651)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33850)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34049)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34248)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34447)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34646)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34845)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35044)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35243)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35442)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35641)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35840)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36039)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36238)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36437)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36636)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36835)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37034)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37233)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37432)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37631)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37830)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38029)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38228)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38427)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38626)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38825)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39024)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39223)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39422)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39621)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39820)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40019)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40218)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40417)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40616)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40815)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41014)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41213)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41412)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41611)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41810)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42009)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42208)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42407)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42606)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42805)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43004)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43203)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43402)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43601)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43800)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43999)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65491)) (i32.const 0)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 65516) (i32.const 40)) + "out of bounds memory access") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65515) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 65515) (i32.const 39)) + "out of bounds memory access") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 20)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65486) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 65486) (i32.const 40)) + "out of bounds memory access") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65487)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65488)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65489)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65490)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65491)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65492)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65493)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65494)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65495)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65496)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65497)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65498)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65499)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65500)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65501)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65502)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65503)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65504)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65505)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65486) (i32.const 65516) (i32.const 40)) + "out of bounds memory access") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65506) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 65506) (i32.const 40)) + "out of bounds memory access") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65507)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65508)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65509)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65510)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65511)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65512)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65513)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65514)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65515)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65506) (i32.const 65516) (i32.const 40)) + "out of bounds memory access") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 65516) (i32.const 40)) + "out of bounds memory access") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 65516) (i32.const 4294963200)) + "out of bounds memory access") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 61440) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 61440) (i32.const 4294967040)) + "out of bounds memory access") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61440)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61441)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 61442)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 61443)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 61444)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 61445)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 61446)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 61447)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 61448)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 61449)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 61450)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 61451)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 61452)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 61453)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 61454)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 61455)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 61456)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 61457)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 61458)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 61459)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 61510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65490)) (i32.const 0)) + +(assert_invalid + (module + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (i64.const 30)))) + "unknown memory 0") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + + +(module + (memory i64 1 1) + (func (export "test") + (memory.fill (i64.const 10) (i32.const 0x55) (i64.const 10)) + (memory.copy (i64.const 9) (i64.const 10) (i64.const 5))) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 9) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 9) (i64.const 20) (i32.const 85)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 20) (i64.const 65536) (i32.const 0)) + (i64.const -1)) + +(module + (memory i64 1 1) + (func (export "test") + (memory.fill (i64.const 10) (i32.const 0x55) (i64.const 10)) + (memory.copy (i64.const 16) (i64.const 15) (i64.const 5))) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 10) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 10) (i64.const 21) (i32.const 85)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 21) (i64.const 65536) (i32.const 0)) + (i64.const -1)) + +(module + (memory i64 1 1) + (func (export "test") + (memory.copy (i64.const 0xFF00) (i64.const 0x8000) (i64.const 257)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1 1) + (func (export "test") + (memory.copy (i64.const 0xFFFFFF00) (i64.const 0x4000) (i64.const 257)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1 1) + (func (export "test") + (memory.copy (i64.const 0x8000) (i64.const 0xFF00) (i64.const 257)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1 1) + (func (export "test") + (memory.copy (i64.const 0x4000) (i64.const 0xFFFFFF00) (i64.const 257)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1 1) + (func (export "test") + (memory.fill (i64.const 0x0000) (i32.const 0x55) (i64.const 0x8000)) + (memory.fill (i64.const 0x8000) (i32.const 0xAA) (i64.const 0x8000)) + (memory.copy (i64.const 0x9000) (i64.const 0x7000) (i64.const 0))) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 32768) (i32.const 85)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 32768) (i64.const 65536) (i32.const 170)) + (i64.const -1)) +(module + (memory i64 1 1) + (func (export "test") + (memory.copy (i64.const 0x10000) (i64.const 0x7000) (i64.const 0)))) +(invoke "test") + +(module + (memory i64 1 1) + (func (export "test") + (memory.copy (i64.const 0x20000) (i64.const 0x7000) (i64.const 0)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1 1) + (func (export "test") + (memory.copy (i64.const 0x9000) (i64.const 0x10000) (i64.const 0)))) +(invoke "test") + +(module + (memory i64 1 1) + (func (export "test") + (memory.copy (i64.const 0x9000) (i64.const 0x20000) (i64.const 0)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1 1) + (func (export "test") + (memory.copy (i64.const 0x10000) (i64.const 0x10000) (i64.const 0)))) +(invoke "test") + +(module + (memory i64 1 1) + (func (export "test") + (memory.copy (i64.const 0x20000) (i64.const 0x20000) (i64.const 0)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1 1) + (func (export "test") + (memory.fill (i64.const 17767) (i32.const 1) (i64.const 1344)) + (memory.fill (i64.const 39017) (i32.const 2) (i64.const 1055)) + (memory.fill (i64.const 56401) (i32.const 3) (i64.const 988)) + (memory.fill (i64.const 37962) (i32.const 4) (i64.const 322)) + (memory.fill (i64.const 7977) (i32.const 5) (i64.const 1994)) + (memory.fill (i64.const 22714) (i32.const 6) (i64.const 3036)) + (memory.fill (i64.const 16882) (i32.const 7) (i64.const 2372)) + (memory.fill (i64.const 43491) (i32.const 8) (i64.const 835)) + (memory.fill (i64.const 124) (i32.const 9) (i64.const 1393)) + (memory.fill (i64.const 2132) (i32.const 10) (i64.const 2758)) + (memory.fill (i64.const 8987) (i32.const 11) (i64.const 3098)) + (memory.fill (i64.const 52711) (i32.const 12) (i64.const 741)) + (memory.fill (i64.const 3958) (i32.const 13) (i64.const 2823)) + (memory.fill (i64.const 49715) (i32.const 14) (i64.const 1280)) + (memory.fill (i64.const 50377) (i32.const 15) (i64.const 1466)) + (memory.fill (i64.const 20493) (i32.const 16) (i64.const 3158)) + (memory.fill (i64.const 47665) (i32.const 17) (i64.const 544)) + (memory.fill (i64.const 12451) (i32.const 18) (i64.const 2669)) + (memory.fill (i64.const 24869) (i32.const 19) (i64.const 2651)) + (memory.fill (i64.const 45317) (i32.const 20) (i64.const 1570)) + (memory.fill (i64.const 43096) (i32.const 21) (i64.const 1691)) + (memory.fill (i64.const 33886) (i32.const 22) (i64.const 646)) + (memory.fill (i64.const 48555) (i32.const 23) (i64.const 1858)) + (memory.fill (i64.const 53453) (i32.const 24) (i64.const 2657)) + (memory.fill (i64.const 30363) (i32.const 25) (i64.const 981)) + (memory.fill (i64.const 9300) (i32.const 26) (i64.const 1807)) + (memory.fill (i64.const 50190) (i32.const 27) (i64.const 487)) + (memory.fill (i64.const 62753) (i32.const 28) (i64.const 530)) + (memory.fill (i64.const 36316) (i32.const 29) (i64.const 943)) + (memory.fill (i64.const 6768) (i32.const 30) (i64.const 381)) + (memory.fill (i64.const 51262) (i32.const 31) (i64.const 3089)) + (memory.fill (i64.const 49729) (i32.const 32) (i64.const 658)) + (memory.fill (i64.const 44540) (i32.const 33) (i64.const 1702)) + (memory.fill (i64.const 33342) (i32.const 34) (i64.const 1092)) + (memory.fill (i64.const 50814) (i32.const 35) (i64.const 1410)) + (memory.fill (i64.const 47594) (i32.const 36) (i64.const 2204)) + (memory.fill (i64.const 54123) (i32.const 37) (i64.const 2394)) + (memory.fill (i64.const 55183) (i32.const 38) (i64.const 250)) + (memory.fill (i64.const 22620) (i32.const 39) (i64.const 2097)) + (memory.fill (i64.const 17132) (i32.const 40) (i64.const 3264)) + (memory.fill (i64.const 54331) (i32.const 41) (i64.const 3299)) + (memory.fill (i64.const 39474) (i32.const 42) (i64.const 2796)) + (memory.fill (i64.const 36156) (i32.const 43) (i64.const 2070)) + (memory.fill (i64.const 35308) (i32.const 44) (i64.const 2763)) + (memory.fill (i64.const 32731) (i32.const 45) (i64.const 312)) + (memory.fill (i64.const 63746) (i32.const 46) (i64.const 192)) + (memory.fill (i64.const 30974) (i32.const 47) (i64.const 596)) + (memory.fill (i64.const 16635) (i32.const 48) (i64.const 501)) + (memory.fill (i64.const 57002) (i32.const 49) (i64.const 686)) + (memory.fill (i64.const 34299) (i32.const 50) (i64.const 385)) + (memory.fill (i64.const 60881) (i32.const 51) (i64.const 903)) + (memory.fill (i64.const 61445) (i32.const 52) (i64.const 2390)) + (memory.fill (i64.const 46972) (i32.const 53) (i64.const 1441)) + (memory.fill (i64.const 25973) (i32.const 54) (i64.const 3162)) + (memory.fill (i64.const 5566) (i32.const 55) (i64.const 2135)) + (memory.fill (i64.const 35977) (i32.const 56) (i64.const 519)) + (memory.fill (i64.const 44892) (i32.const 57) (i64.const 3280)) + (memory.fill (i64.const 46760) (i32.const 58) (i64.const 1678)) + (memory.fill (i64.const 46607) (i32.const 59) (i64.const 3168)) + (memory.fill (i64.const 22449) (i32.const 60) (i64.const 1441)) + (memory.fill (i64.const 58609) (i32.const 61) (i64.const 663)) + (memory.fill (i64.const 32261) (i32.const 62) (i64.const 1671)) + (memory.fill (i64.const 3063) (i32.const 63) (i64.const 721)) + (memory.fill (i64.const 34025) (i32.const 64) (i64.const 84)) + (memory.fill (i64.const 33338) (i32.const 65) (i64.const 2029)) + (memory.fill (i64.const 36810) (i32.const 66) (i64.const 29)) + (memory.fill (i64.const 19147) (i32.const 67) (i64.const 3034)) + (memory.fill (i64.const 12616) (i32.const 68) (i64.const 1043)) + (memory.fill (i64.const 18276) (i32.const 69) (i64.const 3324)) + (memory.fill (i64.const 4639) (i32.const 70) (i64.const 1091)) + (memory.fill (i64.const 16158) (i32.const 71) (i64.const 1997)) + (memory.fill (i64.const 18204) (i32.const 72) (i64.const 2259)) + (memory.fill (i64.const 50532) (i32.const 73) (i64.const 3189)) + (memory.fill (i64.const 11028) (i32.const 74) (i64.const 1968)) + (memory.fill (i64.const 15962) (i32.const 75) (i64.const 1455)) + (memory.fill (i64.const 45406) (i32.const 76) (i64.const 1177)) + (memory.fill (i64.const 54137) (i32.const 77) (i64.const 1568)) + (memory.fill (i64.const 33083) (i32.const 78) (i64.const 1642)) + (memory.fill (i64.const 61028) (i32.const 79) (i64.const 3284)) + (memory.fill (i64.const 51729) (i32.const 80) (i64.const 223)) + (memory.fill (i64.const 4361) (i32.const 81) (i64.const 2171)) + (memory.fill (i64.const 57514) (i32.const 82) (i64.const 1322)) + (memory.fill (i64.const 55724) (i32.const 83) (i64.const 2648)) + (memory.fill (i64.const 24091) (i32.const 84) (i64.const 1045)) + (memory.fill (i64.const 43183) (i32.const 85) (i64.const 3097)) + (memory.fill (i64.const 32307) (i32.const 86) (i64.const 2796)) + (memory.fill (i64.const 3811) (i32.const 87) (i64.const 2010)) + (memory.fill (i64.const 54856) (i32.const 88) (i64.const 0)) + (memory.fill (i64.const 49941) (i32.const 89) (i64.const 2069)) + (memory.fill (i64.const 20411) (i32.const 90) (i64.const 2896)) + (memory.fill (i64.const 33826) (i32.const 91) (i64.const 192)) + (memory.fill (i64.const 9402) (i32.const 92) (i64.const 2195)) + (memory.fill (i64.const 12413) (i32.const 93) (i64.const 24)) + (memory.fill (i64.const 14091) (i32.const 94) (i64.const 577)) + (memory.fill (i64.const 44058) (i32.const 95) (i64.const 2089)) + (memory.fill (i64.const 36735) (i32.const 96) (i64.const 3436)) + (memory.fill (i64.const 23288) (i32.const 97) (i64.const 2765)) + (memory.fill (i64.const 6392) (i32.const 98) (i64.const 830)) + (memory.fill (i64.const 33307) (i32.const 99) (i64.const 1938)) + (memory.fill (i64.const 21941) (i32.const 100) (i64.const 2750)) + (memory.copy (i64.const 59214) (i64.const 54248) (i64.const 2098)) + (memory.copy (i64.const 63026) (i64.const 39224) (i64.const 230)) + (memory.copy (i64.const 51833) (i64.const 23629) (i64.const 2300)) + (memory.copy (i64.const 6708) (i64.const 23996) (i64.const 639)) + (memory.copy (i64.const 6990) (i64.const 33399) (i64.const 1097)) + (memory.copy (i64.const 19403) (i64.const 10348) (i64.const 3197)) + (memory.copy (i64.const 27308) (i64.const 54406) (i64.const 100)) + (memory.copy (i64.const 27221) (i64.const 43682) (i64.const 1717)) + (memory.copy (i64.const 60528) (i64.const 8629) (i64.const 119)) + (memory.copy (i64.const 5947) (i64.const 2308) (i64.const 658)) + (memory.copy (i64.const 4787) (i64.const 51631) (i64.const 2269)) + (memory.copy (i64.const 12617) (i64.const 19197) (i64.const 833)) + (memory.copy (i64.const 11854) (i64.const 46505) (i64.const 3300)) + (memory.copy (i64.const 11376) (i64.const 45012) (i64.const 2281)) + (memory.copy (i64.const 34186) (i64.const 6697) (i64.const 2572)) + (memory.copy (i64.const 4936) (i64.const 1690) (i64.const 1328)) + (memory.copy (i64.const 63164) (i64.const 7637) (i64.const 1670)) + (memory.copy (i64.const 44568) (i64.const 18344) (i64.const 33)) + (memory.copy (i64.const 43918) (i64.const 22348) (i64.const 1427)) + (memory.copy (i64.const 46637) (i64.const 49819) (i64.const 1434)) + (memory.copy (i64.const 63684) (i64.const 8755) (i64.const 834)) + (memory.copy (i64.const 33485) (i64.const 20131) (i64.const 3317)) + (memory.copy (i64.const 40575) (i64.const 54317) (i64.const 3201)) + (memory.copy (i64.const 25812) (i64.const 59254) (i64.const 2452)) + (memory.copy (i64.const 19678) (i64.const 56882) (i64.const 346)) + (memory.copy (i64.const 15852) (i64.const 35914) (i64.const 2430)) + (memory.copy (i64.const 11824) (i64.const 35574) (i64.const 300)) + (memory.copy (i64.const 59427) (i64.const 13957) (i64.const 3153)) + (memory.copy (i64.const 34299) (i64.const 60594) (i64.const 1281)) + (memory.copy (i64.const 8964) (i64.const 12276) (i64.const 943)) + (memory.copy (i64.const 2827) (i64.const 10425) (i64.const 1887)) + (memory.copy (i64.const 43194) (i64.const 43910) (i64.const 738)) + (memory.copy (i64.const 63038) (i64.const 18949) (i64.const 122)) + (memory.copy (i64.const 24044) (i64.const 44761) (i64.const 1755)) + (memory.copy (i64.const 22608) (i64.const 14755) (i64.const 702)) + (memory.copy (i64.const 11284) (i64.const 26579) (i64.const 1830)) + (memory.copy (i64.const 23092) (i64.const 20471) (i64.const 1064)) + (memory.copy (i64.const 57248) (i64.const 54770) (i64.const 2631)) + (memory.copy (i64.const 25492) (i64.const 1025) (i64.const 3113)) + (memory.copy (i64.const 49588) (i64.const 44220) (i64.const 975)) + (memory.copy (i64.const 28280) (i64.const 41722) (i64.const 2336)) + (memory.copy (i64.const 61289) (i64.const 230) (i64.const 2872)) + (memory.copy (i64.const 22480) (i64.const 52506) (i64.const 2197)) + (memory.copy (i64.const 40553) (i64.const 9578) (i64.const 1958)) + (memory.copy (i64.const 29004) (i64.const 20862) (i64.const 2186)) + (memory.copy (i64.const 53029) (i64.const 43955) (i64.const 1037)) + (memory.copy (i64.const 25476) (i64.const 35667) (i64.const 1650)) + (memory.copy (i64.const 58516) (i64.const 45819) (i64.const 1986)) + (memory.copy (i64.const 38297) (i64.const 5776) (i64.const 1955)) + (memory.copy (i64.const 28503) (i64.const 55364) (i64.const 2368)) + (memory.copy (i64.const 62619) (i64.const 18108) (i64.const 1356)) + (memory.copy (i64.const 50149) (i64.const 13861) (i64.const 382)) + (memory.copy (i64.const 16904) (i64.const 36341) (i64.const 1900)) + (memory.copy (i64.const 48098) (i64.const 11358) (i64.const 2807)) + (memory.copy (i64.const 28512) (i64.const 40362) (i64.const 323)) + (memory.copy (i64.const 35506) (i64.const 27856) (i64.const 1670)) + (memory.copy (i64.const 62970) (i64.const 53332) (i64.const 1341)) + (memory.copy (i64.const 14133) (i64.const 46312) (i64.const 644)) + (memory.copy (i64.const 29030) (i64.const 19074) (i64.const 496)) + (memory.copy (i64.const 44952) (i64.const 47577) (i64.const 2784)) + (memory.copy (i64.const 39559) (i64.const 44661) (i64.const 1350)) + (memory.copy (i64.const 10352) (i64.const 29274) (i64.const 1475)) + (memory.copy (i64.const 46911) (i64.const 46178) (i64.const 1467)) + (memory.copy (i64.const 4905) (i64.const 28740) (i64.const 1895)) + (memory.copy (i64.const 38012) (i64.const 57253) (i64.const 1751)) + (memory.copy (i64.const 26446) (i64.const 27223) (i64.const 1127)) + (memory.copy (i64.const 58835) (i64.const 24657) (i64.const 1063)) + (memory.copy (i64.const 61356) (i64.const 38790) (i64.const 766)) + (memory.copy (i64.const 44160) (i64.const 2284) (i64.const 1520)) + (memory.copy (i64.const 32740) (i64.const 47237) (i64.const 3014)) + (memory.copy (i64.const 11148) (i64.const 21260) (i64.const 1011)) + (memory.copy (i64.const 7665) (i64.const 31612) (i64.const 3034)) + (memory.copy (i64.const 18044) (i64.const 12987) (i64.const 3320)) + (memory.copy (i64.const 57306) (i64.const 55905) (i64.const 308)) + (memory.copy (i64.const 24675) (i64.const 16815) (i64.const 1155)) + (memory.copy (i64.const 19900) (i64.const 10115) (i64.const 722)) + (memory.copy (i64.const 2921) (i64.const 5935) (i64.const 2370)) + (memory.copy (i64.const 32255) (i64.const 50095) (i64.const 2926)) + (memory.copy (i64.const 15126) (i64.const 17299) (i64.const 2607)) + (memory.copy (i64.const 45575) (i64.const 28447) (i64.const 2045)) + (memory.copy (i64.const 55149) (i64.const 36113) (i64.const 2596)) + (memory.copy (i64.const 28461) (i64.const 54157) (i64.const 1168)) + (memory.copy (i64.const 47951) (i64.const 53385) (i64.const 3137)) + (memory.copy (i64.const 30646) (i64.const 45155) (i64.const 2649)) + (memory.copy (i64.const 5057) (i64.const 4295) (i64.const 52)) + (memory.copy (i64.const 6692) (i64.const 24195) (i64.const 441)) + (memory.copy (i64.const 32984) (i64.const 27117) (i64.const 3445)) + (memory.copy (i64.const 32530) (i64.const 59372) (i64.const 2785)) + (memory.copy (i64.const 34361) (i64.const 8962) (i64.const 2406)) + (memory.copy (i64.const 17893) (i64.const 54538) (i64.const 3381)) + (memory.copy (i64.const 22685) (i64.const 44151) (i64.const 136)) + (memory.copy (i64.const 59089) (i64.const 7077) (i64.const 1045)) + (memory.copy (i64.const 42945) (i64.const 55028) (i64.const 2389)) + (memory.copy (i64.const 44693) (i64.const 20138) (i64.const 877)) + (memory.copy (i64.const 36810) (i64.const 25196) (i64.const 3447)) + (memory.copy (i64.const 45742) (i64.const 31888) (i64.const 854)) + (memory.copy (i64.const 24236) (i64.const 31866) (i64.const 1377)) + (memory.copy (i64.const 33778) (i64.const 692) (i64.const 1594)) + (memory.copy (i64.const 60618) (i64.const 18585) (i64.const 2987)) + (memory.copy (i64.const 50370) (i64.const 41271) (i64.const 1406)) + ) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 124) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 124) (i64.const 1517) (i32.const 9)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 1517) (i64.const 2132) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 2132) (i64.const 2827) (i32.const 10)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 2827) (i64.const 2921) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 2921) (i64.const 3538) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 3538) (i64.const 3786) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 3786) (i64.const 4042) (i32.const 97)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 4042) (i64.const 4651) (i32.const 99)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 4651) (i64.const 5057) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 5057) (i64.const 5109) (i32.const 99)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 5109) (i64.const 5291) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 5291) (i64.const 5524) (i32.const 72)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 5524) (i64.const 5691) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 5691) (i64.const 6552) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 6552) (i64.const 7133) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 7133) (i64.const 7665) (i32.const 99)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 7665) (i64.const 8314) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 8314) (i64.const 8360) (i32.const 62)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 8360) (i64.const 8793) (i32.const 86)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 8793) (i64.const 8979) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 8979) (i64.const 9373) (i32.const 79)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 9373) (i64.const 9518) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 9518) (i64.const 9934) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 9934) (i64.const 10087) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 10087) (i64.const 10206) (i32.const 5)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 10206) (i64.const 10230) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 10230) (i64.const 10249) (i32.const 41)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 10249) (i64.const 11148) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 11148) (i64.const 11356) (i32.const 74)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 11356) (i64.const 11380) (i32.const 93)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 11380) (i64.const 11939) (i32.const 74)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 11939) (i64.const 12159) (i32.const 68)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 12159) (i64.const 12575) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 12575) (i64.const 12969) (i32.const 79)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 12969) (i64.const 13114) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 13114) (i64.const 14133) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 14133) (i64.const 14404) (i32.const 76)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 14404) (i64.const 14428) (i32.const 57)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 14428) (i64.const 14458) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 14458) (i64.const 14580) (i32.const 32)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 14580) (i64.const 14777) (i32.const 89)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 14777) (i64.const 15124) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 15124) (i64.const 15126) (i32.const 36)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 15126) (i64.const 15192) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 15192) (i64.const 15871) (i32.const 96)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 15871) (i64.const 15998) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 15998) (i64.const 17017) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 17017) (i64.const 17288) (i32.const 76)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 17288) (i64.const 17312) (i32.const 57)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 17312) (i64.const 17342) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 17342) (i64.const 17464) (i32.const 32)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 17464) (i64.const 17661) (i32.const 89)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 17661) (i64.const 17727) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 17727) (i64.const 17733) (i32.const 5)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 17733) (i64.const 17893) (i32.const 96)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 17893) (i64.const 18553) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 18553) (i64.const 18744) (i32.const 42)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 18744) (i64.const 18801) (i32.const 76)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 18801) (i64.const 18825) (i32.const 57)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 18825) (i64.const 18876) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 18876) (i64.const 18885) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 18885) (i64.const 18904) (i32.const 41)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 18904) (i64.const 19567) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 19567) (i64.const 20403) (i32.const 96)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 20403) (i64.const 21274) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 21274) (i64.const 21364) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 21364) (i64.const 21468) (i32.const 74)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 21468) (i64.const 21492) (i32.const 93)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 21492) (i64.const 22051) (i32.const 74)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 22051) (i64.const 22480) (i32.const 68)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 22480) (i64.const 22685) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 22685) (i64.const 22694) (i32.const 68)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 22694) (i64.const 22821) (i32.const 10)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 22821) (i64.const 22869) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 22869) (i64.const 24107) (i32.const 97)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 24107) (i64.const 24111) (i32.const 37)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 24111) (i64.const 24236) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 24236) (i64.const 24348) (i32.const 72)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 24348) (i64.const 24515) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 24515) (i64.const 24900) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 24900) (i64.const 25136) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 25136) (i64.const 25182) (i32.const 85)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 25182) (i64.const 25426) (i32.const 68)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 25426) (i64.const 25613) (i32.const 89)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 25613) (i64.const 25830) (i32.const 96)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 25830) (i64.const 26446) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 26446) (i64.const 26517) (i32.const 10)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 26517) (i64.const 27468) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 27468) (i64.const 27503) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 27503) (i64.const 27573) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 27573) (i64.const 28245) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 28245) (i64.const 28280) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 28280) (i64.const 29502) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 29502) (i64.const 29629) (i32.const 42)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 29629) (i64.const 30387) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 30387) (i64.const 30646) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 30646) (i64.const 31066) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 31066) (i64.const 31131) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 31131) (i64.const 31322) (i32.const 42)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 31322) (i64.const 31379) (i32.const 76)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 31379) (i64.const 31403) (i32.const 57)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 31403) (i64.const 31454) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 31454) (i64.const 31463) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 31463) (i64.const 31482) (i32.const 41)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 31482) (i64.const 31649) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 31649) (i64.const 31978) (i32.const 72)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 31978) (i64.const 32145) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 32145) (i64.const 32530) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 32530) (i64.const 32766) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 32766) (i64.const 32812) (i32.const 85)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 32812) (i64.const 33056) (i32.const 68)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 33056) (i64.const 33660) (i32.const 89)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 33660) (i64.const 33752) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 33752) (i64.const 33775) (i32.const 36)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 33775) (i64.const 33778) (i32.const 32)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 33778) (i64.const 34603) (i32.const 9)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 34603) (i64.const 35218) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 35218) (i64.const 35372) (i32.const 10)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 35372) (i64.const 35486) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 35486) (i64.const 35605) (i32.const 5)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 35605) (i64.const 35629) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 35629) (i64.const 35648) (i32.const 41)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 35648) (i64.const 36547) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 36547) (i64.const 36755) (i32.const 74)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 36755) (i64.const 36767) (i32.const 93)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 36767) (i64.const 36810) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 36810) (i64.const 36839) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 36839) (i64.const 37444) (i32.const 96)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 37444) (i64.const 38060) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 38060) (i64.const 38131) (i32.const 10)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 38131) (i64.const 39082) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 39082) (i64.const 39117) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 39117) (i64.const 39187) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 39187) (i64.const 39859) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 39859) (i64.const 39894) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 39894) (i64.const 40257) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 40257) (i64.const 40344) (i32.const 89)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 40344) (i64.const 40371) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 40371) (i64.const 40804) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 40804) (i64.const 40909) (i32.const 5)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 40909) (i64.const 42259) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 42259) (i64.const 42511) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 42511) (i64.const 42945) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 42945) (i64.const 43115) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 43115) (i64.const 43306) (i32.const 42)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 43306) (i64.const 43363) (i32.const 76)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 43363) (i64.const 43387) (i32.const 57)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 43387) (i64.const 43438) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 43438) (i64.const 43447) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 43447) (i64.const 43466) (i32.const 41)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 43466) (i64.const 44129) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 44129) (i64.const 44958) (i32.const 96)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 44958) (i64.const 45570) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 45570) (i64.const 45575) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 45575) (i64.const 45640) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 45640) (i64.const 45742) (i32.const 42)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 45742) (i64.const 45832) (i32.const 72)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 45832) (i64.const 45999) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 45999) (i64.const 46384) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 46384) (i64.const 46596) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 46596) (i64.const 46654) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 46654) (i64.const 47515) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 47515) (i64.const 47620) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 47620) (i64.const 47817) (i32.const 79)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 47817) (i64.const 47951) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 47951) (i64.const 48632) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 48632) (i64.const 48699) (i32.const 97)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 48699) (i64.const 48703) (i32.const 37)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 48703) (i64.const 49764) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 49764) (i64.const 49955) (i32.const 42)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 49955) (i64.const 50012) (i32.const 76)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 50012) (i64.const 50036) (i32.const 57)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 50036) (i64.const 50087) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 50087) (i64.const 50096) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 50096) (i64.const 50115) (i32.const 41)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 50115) (i64.const 50370) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 50370) (i64.const 51358) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 51358) (i64.const 51610) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 51610) (i64.const 51776) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 51776) (i64.const 51833) (i32.const 89)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 51833) (i64.const 52895) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 52895) (i64.const 53029) (i32.const 97)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 53029) (i64.const 53244) (i32.const 68)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 53244) (i64.const 54066) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 54066) (i64.const 54133) (i32.const 97)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 54133) (i64.const 54137) (i32.const 37)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 54137) (i64.const 55198) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 55198) (i64.const 55389) (i32.const 42)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 55389) (i64.const 55446) (i32.const 76)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 55446) (i64.const 55470) (i32.const 57)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 55470) (i64.const 55521) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 55521) (i64.const 55530) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 55530) (i64.const 55549) (i32.const 41)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 55549) (i64.const 56212) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 56212) (i64.const 57048) (i32.const 96)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 57048) (i64.const 58183) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 58183) (i64.const 58202) (i32.const 41)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 58202) (i64.const 58516) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 58516) (i64.const 58835) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 58835) (i64.const 58855) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 58855) (i64.const 59089) (i32.const 95)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 59089) (i64.const 59145) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 59145) (i64.const 59677) (i32.const 99)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 59677) (i64.const 60134) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 60134) (i64.const 60502) (i32.const 89)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 60502) (i64.const 60594) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 60594) (i64.const 60617) (i32.const 36)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 60617) (i64.const 60618) (i32.const 32)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 60618) (i64.const 60777) (i32.const 42)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 60777) (i64.const 60834) (i32.const 76)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 60834) (i64.const 60858) (i32.const 57)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 60858) (i64.const 60909) (i32.const 59)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 60909) (i64.const 60918) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 60918) (i64.const 60937) (i32.const 41)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 60937) (i64.const 61600) (i32.const 83)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 61600) (i64.const 62436) (i32.const 96)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 62436) (i64.const 63307) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 63307) (i64.const 63397) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 63397) (i64.const 63501) (i32.const 74)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 63501) (i64.const 63525) (i32.const 93)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 63525) (i64.const 63605) (i32.const 74)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 63605) (i64.const 63704) (i32.const 100)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 63704) (i64.const 63771) (i32.const 97)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 63771) (i64.const 63775) (i32.const 37)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 63775) (i64.const 64311) (i32.const 77)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 64311) (i64.const 64331) (i32.const 26)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 64331) (i64.const 64518) (i32.const 92)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 64518) (i64.const 64827) (i32.const 11)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 64827) (i64.const 64834) (i32.const 26)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 64834) (i64.const 65536) (i32.const 0)) + (i64.const -1)) diff --git a/test/core/memory_fill.wast b/test/core/memory_fill.wast index 98374a158..90c6b799f 100644 --- a/test/core/memory_fill.wast +++ b/test/core/memory_fill.wast @@ -684,3 +684,685 @@ (assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) (i32.const -1)) + +(module + (memory i64 1 1) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i64.const 0xFF00) (i32.const 0x55) (i64.const 256)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 65280) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 65280) (i64.const 65536) (i32.const 85)) + (i64.const -1)) +(module + (memory i64 1 1) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i64.const 0xFF00) (i32.const 0x55) (i64.const 257)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1 1) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i64.const 0xFFFFFF00) (i32.const 0x55) (i64.const 257)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1 1) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i64.const 0x12) (i32.const 0x55) (i64.const 0)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 65536) (i32.const 0)) + (i64.const -1)) +(module + (memory i64 1 1) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i64.const 0x10000) (i32.const 0x55) (i64.const 0)))) +(invoke "test") + +(module + (memory i64 1 1) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i64.const 0x20000) (i32.const 0x55) (i64.const 0)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1 1) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i64.const 0x1) (i32.const 0xAA) (i64.const 0xFFFE)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 1) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 1) (i64.const 65535) (i32.const 170)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 65535) (i64.const 65536) (i32.const 0)) + (i64.const -1)) + +(module + (memory i64 1 1) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i64.const 0x12) (i32.const 0x55) (i64.const 10)) + (memory.fill (i64.const 0x15) (i32.const 0xAA) (i64.const 4)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 18) (i32.const 0)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 18) (i64.const 21) (i32.const 85)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 21) (i64.const 25) (i32.const 170)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 25) (i64.const 28) (i32.const 85)) + (i64.const -1)) +(assert_return (invoke "checkRange" (i64.const 28) (i64.const 65536) (i32.const 0)) + (i64.const -1)) +(assert_invalid + (module + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (i64.const 30)))) + "unknown memory 0") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(module + (memory i64 1 1 ) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i64) (param $val i32) (param $len i64) + (memory.fill (local.get $offs) (local.get $val) (local.get $len)))) + +(assert_trap (invoke "run" (i64.const 65280) (i32.const 37) (i64.const 512)) + "out of bounds memory access") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 1) (i32.const 0)) + (i64.const -1)) +(module + (memory i64 1 1 ) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i64) (param $val i32) (param $len i64) + (memory.fill (local.get $offs) (local.get $val) (local.get $len)))) + +(assert_trap (invoke "run" (i64.const 65279) (i32.const 37) (i64.const 514)) + "out of bounds memory access") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 1) (i32.const 0)) + (i64.const -1)) +(module + (memory i64 1 1 ) + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i64) (param $val i32) (param $len i64) + (memory.fill (local.get $offs) (local.get $val) (local.get $len)))) + +(assert_trap (invoke "run" (i64.const 65279) (i32.const 37) (i64.const 4294967295)) + "out of bounds memory access") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 1) (i32.const 0)) + (i64.const -1)) diff --git a/test/core/memory_grow.wast b/test/core/memory_grow.wast index 4b6dbc838..195ef6618 100644 --- a/test/core/memory_grow.wast +++ b/test/core/memory_grow.wast @@ -398,6 +398,26 @@ (assert_return (invoke "as-memory.grow-size") (i32.const 1)) +(module $Mgm + (memory (export "memory") 1) ;; initial size is 1 + (func (export "grow") (result i32) (memory.grow (i32.const 1))) +) +(register "grown-memory" $Mgm) +(assert_return (invoke $Mgm "grow") (i32.const 1)) ;; now size is 2 +(module $Mgim1 + ;; imported memory limits should match, because external memory size is 2 now + (memory (export "memory") (import "grown-memory" "memory") 2) + (func (export "grow") (result i32) (memory.grow (i32.const 1))) +) +(register "grown-imported-memory" $Mgim1) +(assert_return (invoke $Mgim1 "grow") (i32.const 2)) ;; now size is 3 +(module $Mgim2 + ;; imported memory limits should match, because external memory size is 3 now + (import "grown-imported-memory" "memory" (memory 3)) + (func (export "size") (result i32) (memory.size)) +) +(assert_return (invoke $Mgim2 "size") (i32.const 3)) + ;; Multiple memories diff --git a/test/core/memory_grow64.wast b/test/core/memory_grow64.wast new file mode 100644 index 000000000..90d8de607 --- /dev/null +++ b/test/core/memory_grow64.wast @@ -0,0 +1,95 @@ +(module + (memory i64 0) + + (func (export "load_at_zero") (result i32) (i32.load (i64.const 0))) + (func (export "store_at_zero") (i32.store (i64.const 0) (i32.const 2))) + + (func (export "load_at_page_size") (result i32) (i32.load (i64.const 0x10000))) + (func (export "store_at_page_size") (i32.store (i64.const 0x10000) (i32.const 3))) + + (func (export "grow") (param $sz i64) (result i64) (memory.grow (local.get $sz))) + (func (export "size") (result i64) (memory.size)) +) + +(assert_return (invoke "size") (i64.const 0)) +(assert_trap (invoke "store_at_zero") "out of bounds memory access") +(assert_trap (invoke "load_at_zero") "out of bounds memory access") +(assert_trap (invoke "store_at_page_size") "out of bounds memory access") +(assert_trap (invoke "load_at_page_size") "out of bounds memory access") +(assert_return (invoke "grow" (i64.const 1)) (i64.const 0)) +(assert_return (invoke "size") (i64.const 1)) +(assert_return (invoke "load_at_zero") (i32.const 0)) +(assert_return (invoke "store_at_zero")) +(assert_return (invoke "load_at_zero") (i32.const 2)) +(assert_trap (invoke "store_at_page_size") "out of bounds memory access") +(assert_trap (invoke "load_at_page_size") "out of bounds memory access") +(assert_return (invoke "grow" (i64.const 4)) (i64.const 1)) +(assert_return (invoke "size") (i64.const 5)) +(assert_return (invoke "load_at_zero") (i32.const 2)) +(assert_return (invoke "store_at_zero")) +(assert_return (invoke "load_at_zero") (i32.const 2)) +(assert_return (invoke "load_at_page_size") (i32.const 0)) +(assert_return (invoke "store_at_page_size")) +(assert_return (invoke "load_at_page_size") (i32.const 3)) + + +(module + (memory i64 0) + (func (export "grow") (param i64) (result i64) (memory.grow (local.get 0))) +) + +(assert_return (invoke "grow" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "grow" (i64.const 1)) (i64.const 0)) +(assert_return (invoke "grow" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "grow" (i64.const 2)) (i64.const 1)) +(assert_return (invoke "grow" (i64.const 800)) (i64.const 3)) +(assert_return (invoke "grow" (i64.const 1)) (i64.const 803)) + +(module + (memory i64 0 10) + (func (export "grow") (param i64) (result i64) (memory.grow (local.get 0))) +) + +(assert_return (invoke "grow" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "grow" (i64.const 1)) (i64.const 0)) +(assert_return (invoke "grow" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "grow" (i64.const 2)) (i64.const 2)) +(assert_return (invoke "grow" (i64.const 6)) (i64.const 4)) +(assert_return (invoke "grow" (i64.const 0)) (i64.const 10)) +(assert_return (invoke "grow" (i64.const 1)) (i64.const -1)) +(assert_return (invoke "grow" (i64.const 0x10000)) (i64.const -1)) + +;; Test that newly allocated memory (program start and memory.grow) is zeroed + +(module + (memory i64 1) + (func (export "grow") (param i64) (result i64) + (memory.grow (local.get 0)) + ) + (func (export "check-memory-zero") (param i64 i64) (result i32) + (local i32) + (local.set 2 (i32.const 1)) + (block + (loop + (local.set 2 (i32.load8_u (local.get 0))) + (br_if 1 (i32.ne (local.get 2) (i32.const 0))) + (br_if 1 (i64.ge_u (local.get 0) (local.get 1))) + (local.set 0 (i64.add (local.get 0) (i64.const 1))) + (br_if 0 (i64.le_u (local.get 0) (local.get 1))) + ) + ) + (local.get 2) + ) +) + +(assert_return (invoke "check-memory-zero" (i64.const 0) (i64.const 0xffff)) (i32.const 0)) +(assert_return (invoke "grow" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "check-memory-zero" (i64.const 0x10000) (i64.const 0x1_ffff)) (i32.const 0)) +(assert_return (invoke "grow" (i64.const 1)) (i64.const 2)) +(assert_return (invoke "check-memory-zero" (i64.const 0x20000) (i64.const 0x2_ffff)) (i32.const 0)) +(assert_return (invoke "grow" (i64.const 1)) (i64.const 3)) +(assert_return (invoke "check-memory-zero" (i64.const 0x30000) (i64.const 0x3_ffff)) (i32.const 0)) +(assert_return (invoke "grow" (i64.const 1)) (i64.const 4)) +(assert_return (invoke "check-memory-zero" (i64.const 0x40000) (i64.const 0x4_ffff)) (i32.const 0)) +(assert_return (invoke "grow" (i64.const 1)) (i64.const 5)) +(assert_return (invoke "check-memory-zero" (i64.const 0x50000) (i64.const 0x5_ffff)) (i32.const 0)) diff --git a/test/core/memory_init.wast b/test/core/memory_init.wast index 672b1c501..8a3ddbe33 100644 --- a/test/core/memory_init.wast +++ b/test/core/memory_init.wast @@ -965,3 +965,966 @@ (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") (func (memory.init 64 (i32.const 0) (i32.const 0) (i32.const 0)))) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i64.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (nop)) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i64.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 1 (i64.const 7) (i32.const 0) (i32.const 4))) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 8)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i64.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 3 (i64.const 15) (i32.const 1) (i32.const 3))) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 9)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") i64 1 1) + (data (i64.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i64.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 1 (i64.const 7) (i32.const 0) (i32.const 4)) + (data.drop 1) + (memory.init 3 (i64.const 15) (i32.const 1) (i32.const 3)) + (data.drop 3) + (memory.copy (i64.const 20) (i64.const 15) (i64.const 5)) + (memory.copy (i64.const 21) (i64.const 29) (i64.const 1)) + (memory.copy (i64.const 24) (i64.const 10) (i64.const 1)) + (memory.copy (i64.const 13) (i64.const 11) (i64.const 4)) + (memory.copy (i64.const 19) (i64.const 20) (i64.const 5))) + (func (export "load8_u") (param i64) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i64.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i64.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 7)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 8)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 9)) (i32.const 1)) +(assert_return (invoke "load8_u" (i64.const 10)) (i32.const 8)) +(assert_return (invoke "load8_u" (i64.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 13)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 14)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 15)) (i32.const 5)) +(assert_return (invoke "load8_u" (i64.const 16)) (i32.const 2)) +(assert_return (invoke "load8_u" (i64.const 17)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 19)) (i32.const 9)) +(assert_return (invoke "load8_u" (i64.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 21)) (i32.const 7)) +(assert_return (invoke "load8_u" (i64.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 23)) (i32.const 8)) +(assert_return (invoke "load8_u" (i64.const 24)) (i32.const 8)) +(assert_return (invoke "load8_u" (i64.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i64.const 29)) (i32.const 0)) +(assert_invalid + (module + (func (export "test") + (data.drop 0))) + "unknown data segment") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (data.drop 4))) + "unknown data segment") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (data.drop 0) + (data.drop 0))) +(invoke "test") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (data.drop 0) + (memory.init 0 (i64.const 1234) (i32.const 1) (i32.const 1)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1) + (data (i64.const 0) "\37") + (func (export "test") + (memory.init 0 (i64.const 1234) (i32.const 1) (i32.const 1)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(assert_invalid + (module + (func (export "test") + (memory.init 1 (i64.const 1234) (i32.const 1) (i32.const 1)))) + "unknown memory 0") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 1 (i64.const 1234) (i32.const 1) (i32.const 1)))) + "unknown data segment 1") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 0) (i32.const 1)) + (memory.init 0 (i64.const 1) (i32.const 0) (i32.const 1)))) +(invoke "test") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1234) (i32.const 0) (i32.const 5)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1234) (i32.const 2) (i32.const 3)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 0xFFFE) (i32.const 1) (i32.const 3)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1234) (i32.const 4) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1234) (i32.const 1) (i32.const 0)))) +(invoke "test") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 0x10001) (i32.const 0) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 0x10000) (i32.const 0) (i32.const 0)))) +(invoke "test") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 0x10000) (i32.const 1) (i32.const 0)))) +(invoke "test") + +(module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 0x10001) (i32.const 4) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory i64 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(module + (memory i64 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i64) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i64.const 65528) (i32.const 16)) + "out of bounds memory access") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 1) (i32.const 0)) + (i64.const -1)) +(module + (memory i64 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i64) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i64.const 65527) (i32.const 16)) + "out of bounds memory access") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 1) (i32.const 0)) + (i64.const -1)) +(module + (memory i64 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i64) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i64.const 65472) (i32.const 30)) + "out of bounds memory access") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 1) (i32.const 0)) + (i64.const -1)) +(module + (memory i64 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i64) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i64.const 65473) (i32.const 31)) + "out of bounds memory access") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 1) (i32.const 0)) + (i64.const -1)) +(module + (memory i64 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i64) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i64.const 65528) (i32.const 4294967040)) + "out of bounds memory access") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 1) (i32.const 0)) + (i64.const -1)) +(module + (memory i64 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i64) (param $to i64) (param $expected i32) (result i64) + (loop $cont + (if (i64.eq (local.get $from) (local.get $to)) + (then + (return (i64.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i64.add (local.get $from) (i64.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i64) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i64.const 0) (i32.const 4294967292)) + "out of bounds memory access") + +(assert_return (invoke "checkRange" (i64.const 0) (i64.const 1) (i32.const 0)) + (i64.const -1)) + +(module + (memory i64 1) + ;; 65 data segments. 64 is the smallest positive number that is encoded + ;; differently as a signed LEB. + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") + (func (memory.init 64 (i64.const 0) (i32.const 0) (i32.const 0)))) diff --git a/test/core/memory_redundancy64.wast b/test/core/memory_redundancy64.wast new file mode 100644 index 000000000..4aa1ebac1 --- /dev/null +++ b/test/core/memory_redundancy64.wast @@ -0,0 +1,65 @@ +;; Test that optimizers don't do redundant-load, store-to-load, or dead-store +;; optimizations when there are interfering stores, even of different types +;; and to non-identical addresses. + +(module + (memory i64 1 1) + + (func (export "zero_everything") + (i32.store (i64.const 0) (i32.const 0)) + (i32.store (i64.const 4) (i32.const 0)) + (i32.store (i64.const 8) (i32.const 0)) + (i32.store (i64.const 12) (i32.const 0)) + ) + + (func (export "test_store_to_load") (result i32) + (i32.store (i64.const 8) (i32.const 0)) + (f32.store (i64.const 5) (f32.const -0.0)) + (i32.load (i64.const 8)) + ) + + (func (export "test_redundant_load") (result i32) + (local $t i32) + (local $s i32) + (local.set $t (i32.load (i64.const 8))) + (i32.store (i64.const 5) (i32.const 0x80000000)) + (local.set $s (i32.load (i64.const 8))) + (i32.add (local.get $t) (local.get $s)) + ) + + (func (export "test_dead_store") (result f32) + (local $t f32) + (i32.store (i64.const 8) (i32.const 0x23232323)) + (local.set $t (f32.load (i64.const 11))) + (i32.store (i64.const 8) (i32.const 0)) + (local.get $t) + ) + + ;; A function named "malloc" which implementations nonetheless shouldn't + ;; assume behaves like C malloc. + (func $malloc (export "malloc") + (param $size i64) + (result i64) + (i64.const 16) + ) + + ;; Call malloc twice, but unlike C malloc, we don't get non-aliasing pointers. + (func (export "malloc_aliasing") + (result i32) + (local $x i64) + (local $y i64) + (local.set $x (call $malloc (i64.const 4))) + (local.set $y (call $malloc (i64.const 4))) + (i32.store (local.get $x) (i32.const 42)) + (i32.store (local.get $y) (i32.const 43)) + (i32.load (local.get $x)) + ) +) + +(assert_return (invoke "test_store_to_load") (i32.const 0x00000080)) +(invoke "zero_everything") +(assert_return (invoke "test_redundant_load") (i32.const 0x00000080)) +(invoke "zero_everything") +(assert_return (invoke "test_dead_store") (f32.const 0x1.18p-144)) +(invoke "zero_everything") +(assert_return (invoke "malloc_aliasing") (i32.const 43)) diff --git a/test/core/memory_trap64.wast b/test/core/memory_trap64.wast new file mode 100644 index 000000000..78d39d87b --- /dev/null +++ b/test/core/memory_trap64.wast @@ -0,0 +1,269 @@ +(module + (memory i64 1) + + (func $addr_limit (result i64) + (i64.mul (memory.size) (i64.const 0x10000)) + ) + + (func (export "store") (param $i i64) (param $v i32) + (i32.store (i64.add (call $addr_limit) (local.get $i)) (local.get $v)) + ) + + (func (export "load") (param $i i64) (result i32) + (i32.load (i64.add (call $addr_limit) (local.get $i))) + ) + + (func (export "memory.grow") (param i64) (result i64) + (memory.grow (local.get 0)) + ) +) + +(assert_return (invoke "store" (i64.const -4) (i32.const 42))) +(assert_return (invoke "load" (i64.const -4)) (i32.const 42)) +(assert_trap (invoke "store" (i64.const -3) (i32.const 13)) "out of bounds memory access") +(assert_trap (invoke "load" (i64.const -3)) "out of bounds memory access") +(assert_trap (invoke "store" (i64.const -2) (i32.const 13)) "out of bounds memory access") +(assert_trap (invoke "load" (i64.const -2)) "out of bounds memory access") +(assert_trap (invoke "store" (i64.const -1) (i32.const 13)) "out of bounds memory access") +(assert_trap (invoke "load" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "store" (i64.const 0) (i32.const 13)) "out of bounds memory access") +(assert_trap (invoke "load" (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "store" (i64.const 0x80000000) (i32.const 13)) "out of bounds memory access") +(assert_trap (invoke "load" (i64.const 0x80000000)) "out of bounds memory access") + +(module + (memory i64 1) + (data (i64.const 0) "abcdefgh") + (data (i64.const 0xfff8) "abcdefgh") + + (func (export "i32.load") (param $a i64) (result i32) + (i32.load (local.get $a)) + ) + (func (export "i64.load") (param $a i64) (result i64) + (i64.load (local.get $a)) + ) + (func (export "f32.load") (param $a i64) (result f32) + (f32.load (local.get $a)) + ) + (func (export "f64.load") (param $a i64) (result f64) + (f64.load (local.get $a)) + ) + (func (export "i32.load8_s") (param $a i64) (result i32) + (i32.load8_s (local.get $a)) + ) + (func (export "i32.load8_u") (param $a i64) (result i32) + (i32.load8_u (local.get $a)) + ) + (func (export "i32.load16_s") (param $a i64) (result i32) + (i32.load16_s (local.get $a)) + ) + (func (export "i32.load16_u") (param $a i64) (result i32) + (i32.load16_u (local.get $a)) + ) + (func (export "i64.load8_s") (param $a i64) (result i64) + (i64.load8_s (local.get $a)) + ) + (func (export "i64.load8_u") (param $a i64) (result i64) + (i64.load8_u (local.get $a)) + ) + (func (export "i64.load16_s") (param $a i64) (result i64) + (i64.load16_s (local.get $a)) + ) + (func (export "i64.load16_u") (param $a i64) (result i64) + (i64.load16_u (local.get $a)) + ) + (func (export "i64.load32_s") (param $a i64) (result i64) + (i64.load32_s (local.get $a)) + ) + (func (export "i64.load32_u") (param $a i64) (result i64) + (i64.load32_u (local.get $a)) + ) + (func (export "i32.store") (param $a i64) (param $v i32) + (i32.store (local.get $a) (local.get $v)) + ) + (func (export "i64.store") (param $a i64) (param $v i64) + (i64.store (local.get $a) (local.get $v)) + ) + (func (export "f32.store") (param $a i64) (param $v f32) + (f32.store (local.get $a) (local.get $v)) + ) + (func (export "f64.store") (param $a i64) (param $v f64) + (f64.store (local.get $a) (local.get $v)) + ) + (func (export "i32.store8") (param $a i64) (param $v i32) + (i32.store8 (local.get $a) (local.get $v)) + ) + (func (export "i32.store16") (param $a i64) (param $v i32) + (i32.store16 (local.get $a) (local.get $v)) + ) + (func (export "i64.store8") (param $a i64) (param $v i64) + (i64.store8 (local.get $a) (local.get $v)) + ) + (func (export "i64.store16") (param $a i64) (param $v i64) + (i64.store16 (local.get $a) (local.get $v)) + ) + (func (export "i64.store32") (param $a i64) (param $v i64) + (i64.store32 (local.get $a) (local.get $v)) + ) +) + +(assert_trap (invoke "i32.store" (i64.const 0x10000) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store" (i64.const 0xffff) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store" (i64.const 0xfffe) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store" (i64.const 0xfffd) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store" (i64.const -1) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store" (i64.const -2) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store" (i64.const -3) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store" (i64.const -4) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const 0x10000) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const 0xffff) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const 0xfffe) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const 0xfffd) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const 0xfffc) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const 0xfffb) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const 0xfffa) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const 0xfff9) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const -1) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const -2) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const -3) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const -4) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const -5) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const -6) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const -7) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store" (i64.const -8) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f32.store" (i64.const 0x10000) (f32.const 0)) "out of bounds memory access") +(assert_trap (invoke "f32.store" (i64.const 0xffff) (f32.const 0)) "out of bounds memory access") +(assert_trap (invoke "f32.store" (i64.const 0xfffe) (f32.const 0)) "out of bounds memory access") +(assert_trap (invoke "f32.store" (i64.const 0xfffd) (f32.const 0)) "out of bounds memory access") +(assert_trap (invoke "f32.store" (i64.const -1) (f32.const 0)) "out of bounds memory access") +(assert_trap (invoke "f32.store" (i64.const -2) (f32.const 0)) "out of bounds memory access") +(assert_trap (invoke "f32.store" (i64.const -3) (f32.const 0)) "out of bounds memory access") +(assert_trap (invoke "f32.store" (i64.const -4) (f32.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const 0x10000) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const 0xffff) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const 0xfffe) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const 0xfffd) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const 0xfffc) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const 0xfffb) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const 0xfffa) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const 0xfff9) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const -1) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const -2) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const -3) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const -4) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const -5) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const -6) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const -7) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "f64.store" (i64.const -8) (f64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store8" (i64.const 0x10000) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store8" (i64.const -1) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store16" (i64.const 0x10000) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store16" (i64.const 0xffff) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store16" (i64.const -1) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.store16" (i64.const -2) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store8" (i64.const 0x10000) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store8" (i64.const -1) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store16" (i64.const 0x10000) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store16" (i64.const 0xffff) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store16" (i64.const -1) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store16" (i64.const -2) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store32" (i64.const 0x10000) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store32" (i64.const 0xffff) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store32" (i64.const 0xfffe) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store32" (i64.const 0xfffd) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store32" (i64.const -1) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store32" (i64.const -2) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store32" (i64.const -3) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i64.store32" (i64.const -4) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "i32.load" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i32.load" (i64.const 0xffff)) "out of bounds memory access") +(assert_trap (invoke "i32.load" (i64.const 0xfffe)) "out of bounds memory access") +(assert_trap (invoke "i32.load" (i64.const 0xfffd)) "out of bounds memory access") +(assert_trap (invoke "i32.load" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i32.load" (i64.const -2)) "out of bounds memory access") +(assert_trap (invoke "i32.load" (i64.const -3)) "out of bounds memory access") +(assert_trap (invoke "i32.load" (i64.const -4)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const 0xffff)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const 0xfffe)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const 0xfffd)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const 0xfffc)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const 0xfffb)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const 0xfffa)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const 0xfff9)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const -2)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const -3)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const -4)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const -5)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const -6)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const -7)) "out of bounds memory access") +(assert_trap (invoke "i64.load" (i64.const -8)) "out of bounds memory access") +(assert_trap (invoke "f32.load" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "f32.load" (i64.const 0xffff)) "out of bounds memory access") +(assert_trap (invoke "f32.load" (i64.const 0xfffe)) "out of bounds memory access") +(assert_trap (invoke "f32.load" (i64.const 0xfffd)) "out of bounds memory access") +(assert_trap (invoke "f32.load" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "f32.load" (i64.const -2)) "out of bounds memory access") +(assert_trap (invoke "f32.load" (i64.const -3)) "out of bounds memory access") +(assert_trap (invoke "f32.load" (i64.const -4)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const 0xffff)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const 0xfffe)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const 0xfffd)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const 0xfffc)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const 0xfffb)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const 0xfffa)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const 0xfff9)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const -2)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const -3)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const -4)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const -5)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const -6)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const -7)) "out of bounds memory access") +(assert_trap (invoke "f64.load" (i64.const -8)) "out of bounds memory access") +(assert_trap (invoke "i32.load8_s" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i32.load8_s" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i32.load8_u" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i32.load8_u" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i32.load16_s" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i32.load16_s" (i64.const 0xffff)) "out of bounds memory access") +(assert_trap (invoke "i32.load16_s" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i32.load16_s" (i64.const -2)) "out of bounds memory access") +(assert_trap (invoke "i32.load16_u" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i32.load16_u" (i64.const 0xffff)) "out of bounds memory access") +(assert_trap (invoke "i32.load16_u" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i32.load16_u" (i64.const -2)) "out of bounds memory access") +(assert_trap (invoke "i64.load8_s" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i64.load8_s" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i64.load8_u" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i64.load8_u" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i64.load16_s" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i64.load16_s" (i64.const 0xffff)) "out of bounds memory access") +(assert_trap (invoke "i64.load16_s" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i64.load16_s" (i64.const -2)) "out of bounds memory access") +(assert_trap (invoke "i64.load16_u" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i64.load16_u" (i64.const 0xffff)) "out of bounds memory access") +(assert_trap (invoke "i64.load16_u" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i64.load16_u" (i64.const -2)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_s" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_s" (i64.const 0xffff)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_s" (i64.const 0xfffe)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_s" (i64.const 0xfffd)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_s" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_s" (i64.const -2)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_s" (i64.const -3)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_s" (i64.const -4)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_u" (i64.const 0x10000)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_u" (i64.const 0xffff)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_u" (i64.const 0xfffe)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_u" (i64.const 0xfffd)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_u" (i64.const -1)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_u" (i64.const -2)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_u" (i64.const -3)) "out of bounds memory access") +(assert_trap (invoke "i64.load32_u" (i64.const -4)) "out of bounds memory access") + +;; No memory was changed +(assert_return (invoke "i64.load" (i64.const 0xfff8)) (i64.const 0x6867666564636261)) +(assert_return (invoke "i64.load" (i64.const 0)) (i64.const 0x6867666564636261)) diff --git a/test/core/multi-memory/binary0.wast b/test/core/multi-memory/binary0.wast index ff441120f..88270ac9e 100644 --- a/test/core/multi-memory/binary0.wast +++ b/test/core/multi-memory/binary0.wast @@ -47,9 +47,9 @@ (assert_malformed (module binary "\00asm" "\01\00\00\00" - "\05\0a\02" ;; Memory section with 2 entries + "\05\10\02" ;; Memory section with 2 entries "\00\01" ;; no max, minimum 1 - "\00\82\80\80\80\80\00" ;; no max, minimum 2 with one byte too many + "\00\82\80\80\80\80\80\80\80\80\80\80\00" ;; no max, minimum 2 with one byte too many ) "integer representation too long" ) diff --git a/test/core/relaxed-simd/i16x8_relaxed_q15mulr_s.wast b/test/core/relaxed-simd/i16x8_relaxed_q15mulr_s.wast new file mode 100644 index 000000000..00f901cbc --- /dev/null +++ b/test/core/relaxed-simd/i16x8_relaxed_q15mulr_s.wast @@ -0,0 +1,28 @@ +;; Tests for i16x8.relaxed_q15mulr_s. +;; `either` comes from https://github.com/WebAssembly/threads. + +(module + (func (export "i16x8.relaxed_q15mulr_s") (param v128 v128) (result v128) (i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1))) + + (func (export "i16x8.relaxed_q15mulr_s_cmp") (param v128 v128) (result v128) + (i16x8.eq + (i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1)) + (i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1)))) +) + +;; INT16_MIN = -32768 +(assert_return (invoke "i16x8.relaxed_q15mulr_s" + (v128.const i16x8 -32768 -32767 32767 0 0 0 0 0) + (v128.const i16x8 -32768 -32768 32767 0 0 0 0 0)) + ;; overflows, return either INT16_MIN or INT16_MAX + (either (v128.const i16x8 -32768 32767 32766 0 0 0 0 0) + (v128.const i16x8 32767 32767 32766 0 0 0 0 0))) + +;; Check that multiple calls to the relaxed instruction with same inputs returns same results. + +(assert_return (invoke "i16x8.relaxed_q15mulr_s_cmp" + (v128.const i16x8 -32768 -32767 32767 0 0 0 0 0) + (v128.const i16x8 -32768 -32768 32767 0 0 0 0 0)) + ;; overflows, return either INT16_MIN or INT16_MAX + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + diff --git a/test/core/relaxed-simd/i32x4_relaxed_trunc.wast b/test/core/relaxed-simd/i32x4_relaxed_trunc.wast new file mode 100644 index 000000000..cca3ecb95 --- /dev/null +++ b/test/core/relaxed-simd/i32x4_relaxed_trunc.wast @@ -0,0 +1,124 @@ +;; Tests for i32x4.relaxed_trunc_f32x4_s, i32x4.relaxed_trunc_f32x4_u, i32x4.relaxed_trunc_f64x2_s_zero, and i32x4.relaxed_trunc_f64x2_u_zero. +;; `either` comes from https://github.com/WebAssembly/threads. + +(module + (func (export "i32x4.relaxed_trunc_f32x4_s") (param v128) (result v128) (i32x4.relaxed_trunc_f32x4_s (local.get 0))) + (func (export "i32x4.relaxed_trunc_f32x4_u") (param v128) (result v128) (i32x4.relaxed_trunc_f32x4_u (local.get 0))) + (func (export "i32x4.relaxed_trunc_f64x2_s_zero") (param v128) (result v128) (i32x4.relaxed_trunc_f64x2_s_zero (local.get 0))) + (func (export "i32x4.relaxed_trunc_f64x2_u_zero") (param v128) (result v128) (i32x4.relaxed_trunc_f64x2_u_zero (local.get 0))) + + (func (export "i32x4.relaxed_trunc_f32x4_s_cmp") (param v128) (result v128) + (i32x4.eq + (i32x4.relaxed_trunc_f32x4_s (local.get 0)) + (i32x4.relaxed_trunc_f32x4_s (local.get 0)))) + (func (export "i32x4.relaxed_trunc_f32x4_u_cmp") (param v128) (result v128) + (i32x4.eq + (i32x4.relaxed_trunc_f32x4_u (local.get 0)) + (i32x4.relaxed_trunc_f32x4_u (local.get 0)))) + (func (export "i32x4.relaxed_trunc_f64x2_s_zero_cmp") (param v128) (result v128) + (i32x4.eq + (i32x4.relaxed_trunc_f64x2_s_zero (local.get 0)) + (i32x4.relaxed_trunc_f64x2_s_zero (local.get 0)))) + (func (export "i32x4.relaxed_trunc_f64x2_u_zero_cmp") (param v128) (result v128) + (i32x4.eq + (i32x4.relaxed_trunc_f64x2_u_zero (local.get 0)) + (i32x4.relaxed_trunc_f64x2_u_zero (local.get 0)))) +) + +;; Test some edge cases around min/max to ensure that the instruction either +;; saturates correctly or returns INT_MIN. +;; +;; Note, though, that INT_MAX itself is not tested. The value for INT_MAX is +;; 2147483647 but that is not representable in a `f32` since it requires 31 bits +;; when a f32 has only 24 bits available. This means that the closest integers +;; to INT_MAX which can be represented are 2147483520 and 2147483648, meaning +;; that the INT_MAX test case cannot be tested. +(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s" + ;; INT32_MIN INT32_MAX + (v128.const f32x4 -2147483648.0 -2147483904.0 2.0 2147483904.0)) + ;; out of range -> saturate or INT32_MIN + (either (v128.const i32x4 -2147483648 -2147483648 2 2147483647) + (v128.const i32x4 -2147483648 -2147483648 2 -2147483648))) + +(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s" + (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444)) + ;; nans -> 0 or INT32_MIN + (either (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000))) + +(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u" + ;; UINT32_MIN UINT32_MIN-1 saturate or UINT32_MAX + (either (v128.const i32x4 0 0 4294967040 0xffffffff) + (v128.const i32x4 0 0xffffffff 4294967040 0xffffffff))) + +(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u" + (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444)) + ;; nans -> 0 or UINT32_MAX + (either (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff))) + +(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero" + (v128.const f64x2 -2147483904.0 2147483904.0)) + ;; out of range -> saturate or INT32_MIN + (either (v128.const i32x4 -2147483648 2147483647 0 0) + (v128.const i32x4 -2147483648 -2147483648 0 0))) + +(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero" + (v128.const f64x2 nan -nan)) + (either (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0x80000000 0x80000000 0 0))) + +(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero" + (v128.const f64x2 -1.0 4294967296.0)) + ;; out of range -> saturate or UINT32_MAX + (either (v128.const i32x4 0 0xffffffff 0 0) + (v128.const i32x4 0xffffffff 0xffffffff 0 0))) + +(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero" + (v128.const f64x2 nan -nan)) + (either (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0xffffffff 0xffffffff))) + +;; Check that multiple calls to the relaxed instruction with same inputs returns same results. + +(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s_cmp" + ;; INT32_MIN INT32_MAX + (v128.const f32x4 -2147483648.0 -2147483904.0 2147483647.0 2147483904.0)) + ;; out of range -> saturate or INT32_MIN + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s_cmp" + (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444)) + ;; nans -> 0 or INT32_MIN + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u_cmp" + ;; UINT32_MIN UINT32_MIN-1 saturate or UINT32_MAX + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u_cmp" + (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444)) + ;; nans -> 0 or UINT32_MAX + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero_cmp" + (v128.const f64x2 -2147483904.0 2147483904.0)) + ;; out of range -> saturate or INT32_MIN + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero_cmp" + (v128.const f64x2 nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero_cmp" + (v128.const f64x2 -1.0 4294967296.0)) + ;; out of range -> saturate or UINT32_MAX + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero_cmp" + (v128.const f64x2 nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) diff --git a/test/core/relaxed-simd/i8x16_relaxed_swizzle.wast b/test/core/relaxed-simd/i8x16_relaxed_swizzle.wast new file mode 100644 index 000000000..f1bcb4552 --- /dev/null +++ b/test/core/relaxed-simd/i8x16_relaxed_swizzle.wast @@ -0,0 +1,45 @@ +;; Tests for relaxed i8x16 swizzle. +;; `either` comes from https://github.com/WebAssembly/threads. + +(module + (func (export "i8x16.relaxed_swizzle") (param v128 v128) (result v128) (i8x16.relaxed_swizzle (local.get 0) (local.get 1))) + + (func (export "i8x16.relaxed_swizzle_cmp") (param v128 v128) (result v128) + (i8x16.eq + (i8x16.relaxed_swizzle (local.get 0) (local.get 1)) + (i8x16.relaxed_swizzle (local.get 0) (local.get 1)))) +) + +(assert_return (invoke "i8x16.relaxed_swizzle" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (either (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))) + +;; out of range, returns 0 or modulo 15 if < 128 +(assert_return (invoke "i8x16.relaxed_swizzle" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31)) + (either (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))) + +;; out of range, returns 0 if >= 128 +(assert_return (invoke "i8x16.relaxed_swizzle" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 128 129 130 131 132 133 134 135 248 249 250 251 252 253 254 255)) + (either (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))) + +;; Check that multiple calls to the relaxed instruction with same inputs returns same results. + +;; out of range, returns 0 or modulo 15 if < 128 +(assert_return (invoke "i8x16.relaxed_swizzle_cmp" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; out of range, returns 0 if >= 128 +(assert_return (invoke "i8x16.relaxed_swizzle_cmp" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 128 129 130 131 132 133 134 135 248 249 250 251 252 253 254 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) diff --git a/test/core/relaxed-simd/relaxed_dot_product.wast b/test/core/relaxed-simd/relaxed_dot_product.wast new file mode 100644 index 000000000..48714b87b --- /dev/null +++ b/test/core/relaxed-simd/relaxed_dot_product.wast @@ -0,0 +1,107 @@ +;; Tests for relaxed dot products. +;; `either` comes from https://github.com/WebAssembly/threads. + +(module + (func (export "i16x8.relaxed_dot_i8x16_i7x16_s") (param v128 v128) (result v128) (i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1))) + (func (export "i32x4.relaxed_dot_i8x16_i7x16_add_s") (param v128 v128 v128) (result v128) (i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2))) + + (func (export "i16x8.relaxed_dot_i8x16_i7x16_s_cmp") (param v128 v128) (result v128) + (i16x8.eq + (i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1)) + (i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1)))) + (func (export "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp") (param v128 v128 v128) (result v128) + (i16x8.eq + (i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2)) + (i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2)))) +) + +;; Simple values to ensure things are functional. +(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.const i16x8 1 13 41 85 145 221 313 421)) + +;; Test max and min i8 values; +(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s" + (v128.const i8x16 -128 -128 127 127 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 127 127 127 127 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -32512 32258 0 0 0 0 0 0)) + +;; signed * unsigned : -128 * 129 * 2 = -33,024 saturated to -32,768 +;; signed * signed : -128 * -127 * 2 = 32,512 +;; unsigned * unsigned : 128 * 129 * 2 = 33,024 +(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s" + (v128.const i8x16 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (either + (v128.const i16x8 -32768 0 0 0 0 0 0 0) + (v128.const i16x8 32512 0 0 0 0 0 0 0) + (v128.const i16x8 33024 0 0 0 0 0 0 0))) + +;; Simple values to ensure things are functional. +(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i32x4 0 1 2 3)) + ;; intermediate result is [14, 126, 366, 734] + (v128.const i32x4 14 127 368 737)) + +;; Test max and min i8 values; +(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s" + (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 0 0 0 0 0 0 0 0) + (v128.const i8x16 127 127 127 127 127 127 127 127 0 0 0 0 0 0 0 0) + (v128.const i32x4 1 2 3 4)) + ;; intermediate result is [-65024, 64516, 0, 0] + (v128.const i32x4 -65023 64518 3 4)) + +;; signed * unsigned : -128 * 129 * 4 = -66,048 (+ 1) VPDPBUSD AVX2-VNNI or AVX512-VNNI +;; signed * unsigned with intermediate saturation : +;; (-128 * 129) + (-128 * 129) = -33024 saturated to -32768 (PMADDUBSW) +;; -32768 + -32768 = -65536 (+ 1) +;; signed * signed : -128 * -127 * 4 = 65,024 (+ 1) +;; unsigned * unsigned : 128 * 129 * 2 = 66,048 (+ 1) +(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s" + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -127 -127 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 1 2 3 4)) + (either + (v128.const i32x4 -66047 2 3 4) + (v128.const i32x4 -65535 2 3 4) + (v128.const i32x4 65025 2 3 4) + (v128.const i32x4 66049 2 3 4))) + +;; Check that multiple calls to the relaxed instruction with same inputs returns same results. + +;; Test max and min i8 values; +(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s_cmp" + (v128.const i8x16 -128 -128 127 127 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 127 127 127 127 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; Test max and min i8 values; +(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp" + (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 0 0 0 0 0 0 0 0) + (v128.const i8x16 127 127 127 127 127 127 127 127 0 0 0 0 0 0 0 0) + (v128.const i32x4 1 2 3 4)) + ;; intermediate result is [-65024, 64516, 0, 0] + (v128.const i32x4 -1 -1 -1 -1)) + +;; signed * unsigned : -128 * 129 * 2 = -33,024 saturated to -32,768 +;; signed * signed : -128 * -127 * 2 = 32,512 +;; unsigned * unsigned : 128 * 129 * 2 = 33,024 +(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s_cmp" + (v128.const i8x16 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; signed * unsigned : -128 * 129 * 4 = -66,048 (+ 1) VPDPBUSD AVX2-VNNI or AVX512-VNNI +;; signed * unsigned with intermediate saturation : +;; (-128 * 129) + (-128 * 129) = -33024 saturated to -32768 (PMADDUBSW) +;; -32768 + -32768 = -65536 (+ 1) +;; signed * signed : -128 * -127 * 4 = 65,024 (+ 1) +;; unsigned * unsigned : 128 * 129 * 2 = 66,048 (+ 1) +(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp" + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -127 -127 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 1 2 3 4)) + (v128.const i32x4 -1 -1 -1 -1)) diff --git a/test/core/relaxed-simd/relaxed_laneselect.wast b/test/core/relaxed-simd/relaxed_laneselect.wast new file mode 100644 index 000000000..10913816b --- /dev/null +++ b/test/core/relaxed-simd/relaxed_laneselect.wast @@ -0,0 +1,103 @@ +;; Tests for i8x16.relaxed_laneselect, i16x8.relaxed_laneselect, i32x4.relaxed_laneselect, and i64x2.relaxed_laneselect. +;; `either` comes from https://github.com/WebAssembly/threads. + +(module + (func (export "i8x16.relaxed_laneselect") (param v128 v128 v128) (result v128) (i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))) + (func (export "i16x8.relaxed_laneselect") (param v128 v128 v128) (result v128) (i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))) + (func (export "i32x4.relaxed_laneselect") (param v128 v128 v128) (result v128) (i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))) + (func (export "i64x2.relaxed_laneselect") (param v128 v128 v128) (result v128) (i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))) + + (func (export "i8x16.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128) + (i8x16.eq + (i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)) + (i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))) + (func (export "i16x8.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128) + (i16x8.eq + (i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)) + (i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))) + (func (export "i32x4.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128) + (i32x4.eq + (i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)) + (i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))) + (func (export "i64x2.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128) + (i64x2.eq + (i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)) + (i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))) +) + +(assert_return (invoke "i8x16.relaxed_laneselect" + (v128.const i8x16 0 1 0x12 0x12 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 16 17 0x34 0x34 20 21 22 23 24 25 26 27 28 29 30 31) + (v128.const i8x16 0xff 0 0xf0 0x0f 0 0 0 0 0 0 0 0 0 0 0 0)) + (either (v128.const i8x16 0 17 0x14 0x32 20 21 22 23 24 25 26 27 28 29 30 31) + (v128.const i8x16 0 17 0x12 0x34 20 21 22 23 24 25 26 27 28 29 30 31))) + +(assert_return (invoke "i16x8.relaxed_laneselect" + (v128.const i16x8 0 1 0x1234 0x1234 4 5 6 7) + (v128.const i16x8 8 9 0x5678 0x5678 12 13 14 15) + (v128.const i16x8 0xffff 0 0xff00 0x00ff 0 0 0 0)) + (either (v128.const i16x8 0 9 0x1278 0x5634 12 13 14 15) + (v128.const i16x8 0 9 0x1234 0x5678 12 13 14 15))) + +;; special case for i16x8 to allow pblendvb +(assert_return (invoke "i16x8.relaxed_laneselect" + (v128.const i16x8 0 1 0x1234 0x1234 4 5 6 7) + (v128.const i16x8 8 9 0x5678 0x5678 12 13 14 15) + (v128.const i16x8 0xffff 0 0xff00 0x0080 0 0 0 0)) ;; 0x0080 is the special case + (either (v128.const i16x8 0 9 0x1278 0x5678 12 13 14 15) ;; bitselect + (v128.const i16x8 0 9 0x1234 0x5678 12 13 14 15) ;; top bit of i16 lane examined + (v128.const i16x8 0 9 0x1278 0x5634 12 13 14 15) ;; top bit of each byte + )) + +(assert_return (invoke "i32x4.relaxed_laneselect" + (v128.const i32x4 0 1 0x12341234 0x12341234) + (v128.const i32x4 4 5 0x56785678 0x56785678) + (v128.const i32x4 0xffffffff 0 0xffff0000 0x0000ffff)) + (either (v128.const i32x4 0 5 0x12345678 0x56781234) + (v128.const i32x4 0 5 0x12341234 0x56785678))) + +(assert_return (invoke "i64x2.relaxed_laneselect" + (v128.const i64x2 0 1) + (v128.const i64x2 2 3) + (v128.const i64x2 0xffffffffffffffff 0)) + (either (v128.const i64x2 0 3) + (v128.const i64x2 0 3))) + +(assert_return (invoke "i64x2.relaxed_laneselect" + (v128.const i64x2 0x1234123412341234 0x1234123412341234) + (v128.const i64x2 0x5678567856785678 0x5678567856785678) + (v128.const i64x2 0xffffffff00000000 0x00000000ffffffff)) + (either (v128.const i64x2 0x1234123456785678 0x5678567812341234) + (v128.const i64x2 0x1234123412341234 0x5678567856785678))) + +;; Check that multiple calls to the relaxed instruction with same inputs returns same results. + +(assert_return (invoke "i8x16.relaxed_laneselect_cmp" + (v128.const i8x16 0 1 0x12 0x12 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 16 17 0x34 0x34 20 21 22 23 24 25 26 27 28 29 30 31) + (v128.const i8x16 0xff 0 0xf0 0x0f 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +(assert_return (invoke "i16x8.relaxed_laneselect_cmp" + (v128.const i16x8 0 1 0x1234 0x1234 4 5 6 7) + (v128.const i16x8 8 9 0x5678 0x5678 12 13 14 15) + (v128.const i16x8 0xffff 0 0xff00 0x00ff 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +(assert_return (invoke "i32x4.relaxed_laneselect_cmp" + (v128.const i32x4 0 1 0x12341234 0x12341234) + (v128.const i32x4 4 5 0x56785678 0x56785678) + (v128.const i32x4 0xffffffff 0 0xffff0000 0x0000ffff)) + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "i64x2.relaxed_laneselect_cmp" + (v128.const i64x2 0 1) + (v128.const i64x2 2 3) + (v128.const i64x2 0xffffffffffffffff 0)) + (v128.const i64x2 -1 -1)) + +(assert_return (invoke "i64x2.relaxed_laneselect_cmp" + (v128.const i64x2 0x1234123412341234 0x1234123412341234) + (v128.const i64x2 0x5678567856785678 0x5678567856785678) + (v128.const i64x2 0xffffffff00000000 0x00000000ffffffff)) + (v128.const i64x2 -1 -1)) diff --git a/test/core/relaxed-simd/relaxed_madd_nmadd.wast b/test/core/relaxed-simd/relaxed_madd_nmadd.wast new file mode 100644 index 000000000..187b71d5a --- /dev/null +++ b/test/core/relaxed-simd/relaxed_madd_nmadd.wast @@ -0,0 +1,224 @@ +;; Tests for f32x4.relaxed_madd, f32x4.relaxed_nmadd, f64x2.relaxed_madd, and f64x2.relaxed_nmadd. +;; `either` comes from https://github.com/WebAssembly/threads. + +(module + (func (export "f32x4.relaxed_madd") (param v128 v128 v128) (result v128) (f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2))) + (func (export "f32x4.relaxed_nmadd") (param v128 v128 v128) (result v128) (f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))) + (func (export "f64x2.relaxed_nmadd") (param v128 v128 v128) (result v128) (f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))) + (func (export "f64x2.relaxed_madd") (param v128 v128 v128) (result v128) (f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2))) + + (func (export "f32x4.relaxed_madd_cmp") (param v128 v128 v128) (result v128) + (f32x4.eq + (f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2)) + (f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2)))) + (func (export "f32x4.relaxed_nmadd_cmp") (param v128 v128 v128) (result v128) + (f32x4.eq + (f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2)) + (f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2)))) + (func (export "f64x2.relaxed_nmadd_cmp") (param v128 v128 v128) (result v128) + (f64x2.eq + (f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2)) + (f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2)))) + (func (export "f64x2.relaxed_madd_cmp") (param v128 v128 v128) (result v128) + (f64x2.eq + (f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2)) + (f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2)))) +) + + +;; FLT_MAX == 0x1.fffffep+127 +;; FLT_MAX * 2 - FLT_MAX == +;; FLT_MAX (if fma) +;; 0 (if no fma) +;; from https://www.vinc17.net/software/fma-tests.c +(assert_return (invoke "f32x4.relaxed_madd" + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 ) + (v128.const f32x4 2.0 2.0 2.0 2.0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (either (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf))) + +;; Special values for float: +;; x = 0x1.000004p+0 (1 + 2^-22) +;; y = 0x1.0002p+0 (1 + 2^-15) +;; z = -(1.0 + 0x0.0002p+0 + 0x0.000004p+0) +;; = -0x1.000204p+0 +;; x.y = 1.0 + 0x0.0002p+0 + 0x0.000004p+0 + 0x1p-37 (round bit) +;; x.y+z = 0 (2 roundings) +;; fma(x, y, z) = (0x1p-37) 2^-37 +;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information +(assert_return (invoke "f32x4.relaxed_madd" + (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0) + (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0) + (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0)) + (either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37) + (v128.const f32x4 0 0 0 0))) +;; nmadd tests with negated x, same answers are expected. +(assert_return (invoke "f32x4.relaxed_nmadd" + (v128.const f32x4 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0) + (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0) + (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0)) + (either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37) + (v128.const f32x4 0 0 0 0))) +;; nmadd tests with negated y, same answers are expected. +(assert_return (invoke "f32x4.relaxed_nmadd" + (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0) + (v128.const f32x4 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0) + (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0)) + (either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37) + (v128.const f32x4 0 0 0 0))) + +;; DBL_MAX = 0x1.fffffffffffffp+1023 +;; DLB_MAX * 2 - DLB_MAX == +;; DLB_MAX (if fma) +;; 0 (if no fma) +;; form https://www.vinc17.net/software/fma-tests.c +;; from https://www.vinc17.net/software/fma-tests.c +(assert_return (invoke "f64x2.relaxed_madd" + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 2.0 2.0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (either (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf))) + +;; Special values for double: +;; x = 0x1.00000004p+0 (1 + 2^-30) +;; y = 0x1.000002p+0 (1 + 2^-23) +;; z = -(1.0 + 0x0.000002p+0 + 0x0.00000004p+0) +;; = -0x1.00000204p+0 +;; x.y = 1.0 + 0x0.000002p+0 + 0x0.00000004p+0 + 0x1p-53 (round bit) +;; x.y+z = 0 (2 roundings) +;; fma(x, y, z) = 0x1p-53 +;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information +(assert_return (invoke "f64x2.relaxed_madd" + (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0) + (v128.const f64x2 0x1.000002p+0 0x1.000002p+0) + (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0)) + (either (v128.const f64x2 0x1p-53 0x1p-53) + (v128.const f64x2 0 0))) +;; nmadd tests with negated x, same answers are expected. +(assert_return (invoke "f64x2.relaxed_nmadd" + (v128.const f64x2 -0x1.00000004p+0 -0x1.00000004p+0) + (v128.const f64x2 0x1.000002p+0 0x1.000002p+0) + (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0)) + (either (v128.const f64x2 0x1p-53 0x1p-53) + (v128.const f64x2 0 0))) +;; nmadd tests with negated y, same answers are expected. +(assert_return (invoke "f64x2.relaxed_nmadd" + (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0) + (v128.const f64x2 -0x1.000002p+0 -0x1.000002p+0) + (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0)) + (either (v128.const f64x2 0x1p-53 0x1p-53) + (v128.const f64x2 0 0))) + +;; Check that multiple calls to the relaxed instruction with same inputs returns same results. + +;; FLT_MAX == 0x1.fffffep+127 +;; FLT_MAX * 2 - FLT_MAX == +;; FLT_MAX (if fma) +;; 0 (if no fma) +;; from https://www.vinc17.net/software/fma-tests.c +(assert_return (invoke "f32x4.relaxed_madd_cmp" + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 ) + (v128.const f32x4 2.0 2.0 2.0 2.0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; Special values for float: +;; x = 0x1.000004p+0 (1 + 2^-22) +;; y = 0x1.0002p+0 (1 + 2^-15) +;; z = -(1.0 + 0x0.0002p+0 + 0x0.000004p+0) +;; = -0x1.000204p+0 +;; x.y = 1.0 + 0x0.0002p+0 + 0x0.000004p+0 + 0x1p-37 (round bit) +;; x.y+z = 0 (2 roundings) +;; fma(x, y, z) = (0x1p-37) 2^-37 +;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information +(assert_return (invoke "f32x4.relaxed_madd_cmp" + (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0) + (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0) + (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +;; nmadd tests with negated x, same answers are expected. +(assert_return (invoke "f32x4.relaxed_nmadd_cmp" + (v128.const f32x4 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0) + (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0) + (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +;; nmadd tests with negated y, same answers are expected. +(assert_return (invoke "f32x4.relaxed_nmadd_cmp" + (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0) + (v128.const f32x4 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0) + (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; DBL_MAX = 0x1.fffffffffffffp+1023 +;; DLB_MAX * 2 - DLB_MAX == +;; DLB_MAX (if fma) +;; 0 (if no fma) +;; form https://www.vinc17.net/software/fma-tests.c +;; from https://www.vinc17.net/software/fma-tests.c +(assert_return (invoke "f64x2.relaxed_madd_cmp" + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 2.0 2.0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) + +;; Special values for double: +;; x = 0x1.00000004p+0 (1 + 2^-30) +;; y = 0x1.000002p+0 (1 + 2^-23) +;; z = -(1.0 + 0x0.000002p+0 + 0x0.00000004p+0) +;; = -0x1.00000204p+0 +;; x.y = 1.0 + 0x0.000002p+0 + 0x0.00000004p+0 + 0x1p-53 (round bit) +;; x.y+z = 0 (2 roundings) +;; fma(x, y, z) = 0x1p-53 +;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information +(assert_return (invoke "f64x2.relaxed_madd_cmp" + (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0) + (v128.const f64x2 0x1.000002p+0 0x1.000002p+0) + (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0)) + (v128.const i64x2 -1 -1)) +;; nmadd tests with negated x, same answers are expected. +(assert_return (invoke "f64x2.relaxed_nmadd_cmp" + (v128.const f64x2 -0x1.00000004p+0 -0x1.00000004p+0) + (v128.const f64x2 0x1.000002p+0 0x1.000002p+0) + (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0)) + (v128.const i64x2 -1 -1)) +;; nmadd tests with negated y, same answers are expected. +(assert_return (invoke "f64x2.relaxed_nmadd_cmp" + (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0) + (v128.const f64x2 -0x1.000002p+0 -0x1.000002p+0) + (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0)) + (v128.const i64x2 -1 -1)) + +;; Test that the non-deterministic choice of fusing and then rounding or +;; rounding multiple times in `relaxed_madd` is consistent throughout a +;; program's execution. +;; +;; This property is impossible to test exhaustively, so this is just a simple +;; smoke test for when the operands to a `relaxed_madd` are known statically +;; versus when they are dynamically supplied. This should, at least, catch +;; illegal constant-folding and -propagation by the compiler that leads to +;; inconsistent rounding behavior at compile time versus at run time. +;; +;; FLT_MAX == 0x1.fffffep+127 +;; FLT_MAX * 2 - FLT_MAX == +;; FLT_MAX (if fma) +;; 0 (if no fma) +;; from https://www.vinc17.net/software/fma-tests.c +(module + (func (export "test-consistent-nondeterminism") (param v128 v128 v128) (result v128) + (f32x4.eq + (f32x4.relaxed_madd (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 ) + (v128.const f32x4 2.0 2.0 2.0 2.0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (f32x4.relaxed_madd (local.get 0) + (local.get 1) + (local.get 2)) + ) + ) +) +(assert_return (invoke "test-consistent-nondeterminism" + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 ) + (v128.const f32x4 2.0 2.0 2.0 2.0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) diff --git a/test/core/relaxed-simd/relaxed_min_max.wast b/test/core/relaxed-simd/relaxed_min_max.wast new file mode 100644 index 000000000..ac3ebb07c --- /dev/null +++ b/test/core/relaxed-simd/relaxed_min_max.wast @@ -0,0 +1,184 @@ +;; Tests for f32x4.min, f32x4.max, f64x2.min, and f64x2.max. +;; `either` comes from https://github.com/WebAssembly/threads. + +(module + (func (export "f32x4.relaxed_min") (param v128 v128) (result v128) (f32x4.relaxed_min (local.get 0) (local.get 1))) + (func (export "f32x4.relaxed_max") (param v128 v128) (result v128) (f32x4.relaxed_max (local.get 0) (local.get 1))) + (func (export "f64x2.relaxed_min") (param v128 v128) (result v128) (f64x2.relaxed_min (local.get 0) (local.get 1))) + (func (export "f64x2.relaxed_max") (param v128 v128) (result v128) (f64x2.relaxed_max (local.get 0) (local.get 1))) + + (func (export "f32x4.relaxed_min_cmp") (param v128 v128) (result v128) + (i32x4.eq + (f32x4.relaxed_min (local.get 0) (local.get 1)) + (f32x4.relaxed_min (local.get 0) (local.get 1)))) + (func (export "f32x4.relaxed_max_cmp") (param v128 v128) (result v128) + (i32x4.eq + (f32x4.relaxed_max (local.get 0) (local.get 1)) + (f32x4.relaxed_max (local.get 0) (local.get 1)))) + (func (export "f64x2.relaxed_min_cmp") (param v128 v128) (result v128) + (i64x2.eq + (f64x2.relaxed_min (local.get 0) (local.get 1)) + (f64x2.relaxed_min (local.get 0) (local.get 1)))) + (func (export "f64x2.relaxed_max_cmp") (param v128 v128) (result v128) + (i64x2.eq + (f64x2.relaxed_max (local.get 0) (local.get 1)) + (f64x2.relaxed_max (local.get 0) (local.get 1)))) +) + +(assert_return (invoke "f32x4.relaxed_min" + (v128.const f32x4 -nan nan 0 0) + (v128.const f32x4 0 0 -nan nan)) + (either (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical) + (v128.const f32x4 nan:canonical nan:canonical 0 0) + (v128.const f32x4 0 0 nan:canonical nan:canonical) + (v128.const f32x4 0 0 0 0))) + +(assert_return (invoke "f32x4.relaxed_min" + (v128.const f32x4 +0.0 -0.0 +0.0 -0.0) + (v128.const f32x4 -0.0 +0.0 +0.0 -0.0)) + (either (v128.const f32x4 -0.0 -0.0 +0.0 -0.0) + (v128.const f32x4 +0.0 -0.0 +0.0 -0.0) + (v128.const f32x4 -0.0 +0.0 +0.0 -0.0) + (v128.const f32x4 -0.0 -0.0 +0.0 -0.0))) + +(assert_return (invoke "f32x4.relaxed_max" + (v128.const f32x4 -nan nan 0 0) + (v128.const f32x4 0 0 -nan nan)) + (either (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical) + (v128.const f32x4 nan:canonical nan:canonical 0 0) + (v128.const f32x4 0 0 nan:canonical nan:canonical) + (v128.const f32x4 0 0 0 0))) + +(assert_return (invoke "f32x4.relaxed_max" + (v128.const f32x4 +0.0 -0.0 +0.0 -0.0) + (v128.const f32x4 -0.0 +0.0 +0.0 -0.0)) + (either (v128.const f32x4 +0.0 +0.0 +0.0 -0.0) + (v128.const f32x4 +0.0 -0.0 +0.0 -0.0) + (v128.const f32x4 -0.0 +0.0 +0.0 -0.0) + (v128.const f32x4 -0.0 -0.0 +0.0 -0.0))) + +(assert_return (invoke "f64x2.relaxed_min" + (v128.const f64x2 -nan nan) + (v128.const f64x2 0 0)) + (either (v128.const f64x2 nan:canonical nan:canonical) + (v128.const f64x2 nan:canonical nan:canonical) + (v128.const f64x2 0 0) + (v128.const f64x2 0 0))) + +(assert_return (invoke "f64x2.relaxed_min" + (v128.const f64x2 0 0) + (v128.const f64x2 -nan nan)) + (either (v128.const f64x2 nan:canonical nan:canonical) + (v128.const f64x2 0 0) + (v128.const f64x2 nan:canonical nan:canonical) + (v128.const f64x2 0 0))) + +(assert_return (invoke "f64x2.relaxed_min" + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 -0.0 +0.0)) + (either (v128.const f64x2 -0.0 -0.0) + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 -0.0 +0.0) + (v128.const f64x2 -0.0 -0.0))) + +(assert_return (invoke "f64x2.relaxed_min" + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 +0.0 -0.0)) + (either (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 +0.0 -0.0))) + +(assert_return (invoke "f64x2.relaxed_max" + (v128.const f64x2 -nan nan) + (v128.const f64x2 0 0)) + (either (v128.const f64x2 nan:canonical nan:canonical) + (v128.const f64x2 nan:canonical nan:canonical) + (v128.const f64x2 0 0) + (v128.const f64x2 0 0))) + +(assert_return (invoke "f64x2.relaxed_max" + (v128.const f64x2 0 0) + (v128.const f64x2 -nan nan)) + (either (v128.const f64x2 nan:canonical nan:canonical) + (v128.const f64x2 0 0) + (v128.const f64x2 nan:canonical nan:canonical) + (v128.const f64x2 0 0))) + +(assert_return (invoke "f64x2.relaxed_max" + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 -0.0 +0.0)) + (either (v128.const f64x2 +0.0 +0.0) + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 -0.0 +0.0) + (v128.const f64x2 -0.0 -0.0))) + +(assert_return (invoke "f64x2.relaxed_max" + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 +0.0 -0.0)) + (either (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 +0.0 -0.0))) + +;; Check that multiple calls to the relaxed instruction with same inputs returns same results. + +(assert_return (invoke "f32x4.relaxed_min_cmp" + (v128.const f32x4 -nan nan 0 0) + (v128.const f32x4 0 0 -nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "f32x4.relaxed_min_cmp" + (v128.const f32x4 +0.0 -0.0 +0.0 -0.0) + (v128.const f32x4 -0.0 +0.0 +0.0 -0.0)) + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "f32x4.relaxed_max_cmp" + (v128.const f32x4 -nan nan 0 0) + (v128.const f32x4 0 0 -nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "f32x4.relaxed_max_cmp" + (v128.const f32x4 +0.0 -0.0 +0.0 -0.0) + (v128.const f32x4 -0.0 +0.0 +0.0 -0.0)) + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "f64x2.relaxed_min_cmp" + (v128.const f64x2 -nan nan) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) + +(assert_return (invoke "f64x2.relaxed_min_cmp" + (v128.const f64x2 0 0) + (v128.const f64x2 -nan nan)) + (v128.const i64x2 -1 -1)) + +(assert_return (invoke "f64x2.relaxed_min_cmp" + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 -0.0 +0.0)) + (v128.const i64x2 -1 -1)) + +(assert_return (invoke "f64x2.relaxed_min_cmp" + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 +0.0 -0.0)) + (v128.const i64x2 -1 -1)) + +(assert_return (invoke "f64x2.relaxed_max_cmp" + (v128.const f64x2 -nan nan) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) + +(assert_return (invoke "f64x2.relaxed_max_cmp" + (v128.const f64x2 0 0) + (v128.const f64x2 -nan nan)) + (v128.const i64x2 -1 -1)) + +(assert_return (invoke "f64x2.relaxed_max_cmp" + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 -0.0 +0.0)) + (v128.const i64x2 -1 -1)) + +(assert_return (invoke "f64x2.relaxed_max_cmp" + (v128.const f64x2 +0.0 -0.0) + (v128.const f64x2 +0.0 -0.0)) + (v128.const i64x2 -1 -1)) diff --git a/test/core/return_call.wast b/test/core/return_call.wast index 2f91f4dea..b9e8f8f01 100644 --- a/test/core/return_call.wast +++ b/test/core/return_call.wast @@ -188,7 +188,15 @@ ) "type mismatch" ) - +(assert_invalid + (module + (func $f (result i32 i32) unreachable) + (func (result i32) + return_call $f + ) + ) + "type mismatch" +) ;; Unbound function diff --git a/test/core/return_call_indirect.wast b/test/core/return_call_indirect.wast index acf0a72e0..aa158be25 100644 --- a/test/core/return_call_indirect.wast +++ b/test/core/return_call_indirect.wast @@ -508,7 +508,17 @@ ) "type mismatch" ) - +(assert_invalid + (module + (type $ty (func (result i32 i32))) + (import "env" "table" (table $table 0 funcref)) + (func (param i32) (result i32) + local.get 0 + return_call_indirect $table (type $ty) + ) + ) + "type mismatch" +) ;; Unbound type diff --git a/test/core/return_call_ref.wast b/test/core/return_call_ref.wast index 2b495f46e..5f5a7cba7 100644 --- a/test/core/return_call_ref.wast +++ b/test/core/return_call_ref.wast @@ -374,3 +374,24 @@ ) "type mismatch" ) + +(assert_invalid + (module + (type $t (func)) + (func $f (param $r funcref) + (return_call_ref $t (local.get $r)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $ty (func (result i32 i32))) + (func (param (ref $ty)) (result i32) + local.get 0 + return_call_ref $ty + ) + ) + "type mismatch" +) diff --git a/test/core/run.py b/test/core/run.py index bdc2a447a..1404929fc 100755 --- a/test/core/run.py +++ b/test/core/run.py @@ -14,12 +14,18 @@ inputDir = ownDir interpDir = os.path.join(os.path.dirname(os.path.dirname(ownDir)), 'interpreter') outputDir = os.path.join(inputDir, "_output") +opts = "" + +mainTestFiles = glob.glob(os.path.join(inputDir, "*.wast")) +otherTestFiles = glob.glob(os.path.join(inputDir, "[a-z]*/*.wast")) parser = argparse.ArgumentParser() parser.add_argument("--wasm", metavar="", default=os.path.join(interpDir, "wasm")) parser.add_argument("--js", metavar="") parser.add_argument("--generate-js-only", action='store_true') +parser.add_argument("--failfast", action='store_true') parser.add_argument("--out", metavar="", default=outputDir) +parser.add_argument("--opts", metavar="", default=opts) parser.add_argument("file", nargs='*') arguments = parser.parse_args() sys.argv = sys.argv[:1] @@ -27,17 +33,20 @@ main_test_files = glob.glob(os.path.join(inputDir, "*.wast")) # Other test files are in subdirectories simd_test_files = glob.glob(os.path.join(inputDir, "simd", "*.wast")) +relaxed_simd_test_files = glob.glob(os.path.join(inputDir, "relaxed-simd", "*.wast")) gc_test_files = glob.glob(os.path.join(inputDir, "gc", "*.wast")) multi_memory_test_files = glob.glob(os.path.join(inputDir, "multi-memory", "*.wast")) -all_test_files = main_test_files + simd_test_files + gc_test_files + multi_memory_test_files +stack_switching_test_files = glob.glob(os.path.join(inputDir, "stack-switching", "*.wast")) +all_test_files = main_test_files + simd_test_files + relaxed_simd_test_files + gc_test_files + multi_memory_test_files + stack_switching_test_files -wasmCommand = arguments.wasm +wasmExec = arguments.wasm +wasmCommand = wasmExec + " " + arguments.opts jsCommand = arguments.js generateJsOnly = arguments.generate_js_only outputDir = arguments.out inputFiles = arguments.file if arguments.file else all_test_files -if not os.path.exists(wasmCommand): +if not os.path.exists(wasmExec): sys.stderr.write("""\ Error: The executable '%s' does not exist. Provide the correct path with the '--wasm' flag. @@ -51,7 +60,10 @@ class RunTests(unittest.TestCase): def _runCommand(self, command, logPath, expectedExitCode = 0): with open(logPath, 'w+') as out: exitCode = subprocess.call(command, shell=True, stdout=out, stderr=subprocess.STDOUT) - self.assertEqual(expectedExitCode, exitCode, "failed with exit code %i (expected %i) for %s" % (exitCode, expectedExitCode, command)) + with open(logPath) as out: + log = out.read() + msg = "failed with exit code %i (expected %i)\nCommand:\n %s\nLog:\n%s" + self.assertEqual(expectedExitCode, exitCode, msg % (exitCode, expectedExitCode, command, log)) def _auxFile(self, path): if os.path.exists(path): @@ -64,7 +76,7 @@ def _compareFile(self, expectFile, actualFile): with open(actualFile) as actual: expectText = expect.read() actualText = actual.read() - self.assertEqual(expectText, actualText) + self.assertEqual(expectText, actualText) def _runTestFile(self, inputPath): dir, inputFile = os.path.split(inputPath) @@ -120,4 +132,4 @@ def _runTestFile(self, inputPath): for fileName in inputFiles: testName = 'test ' + os.path.basename(fileName) setattr(RunTests, testName, lambda self, file=fileName: self._runTestFile(file)) - unittest.main() + unittest.main(failfast=arguments.failfast) diff --git a/test/core/simd/simd_address.wast b/test/core/simd/simd_address.wast index 9e023008b..56a5e4567 100644 --- a/test/core/simd/simd_address.wast +++ b/test/core/simd/simd_address.wast @@ -140,18 +140,18 @@ ;; Offset constant out of range -(assert_malformed +(assert_invalid (module quote "(memory 1)" "(func (drop (v128.load offset=4294967296 (i32.const 0))))" ) - "i32 constant" + "offset out of range" ) -(assert_malformed +(assert_invalid (module quote "(memory 1)" "(func (v128.store offset=4294967296 (i32.const 0) (v128.const i32x4 0 0 0 0)))" ) - "i32 constant" + "offset out of range" ) diff --git a/test/core/simd/simd_lane.wast b/test/core/simd/simd_lane.wast index 9b66f53b1..3dbd0c9f1 100644 --- a/test/core/simd/simd_lane.wast +++ b/test/core/simd/simd_lane.wast @@ -602,23 +602,23 @@ "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "invalid lane length") (assert_malformed (module quote "(func (result v128) " - "(i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15.0) " + "(i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15.0 " "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") (assert_malformed (module quote "(func (result v128) " - "(i8x16.shuffle 0.5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) " + "(i8x16.shuffle 0.5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 " "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") (assert_malformed (module quote "(func (result v128) " - "(i8x16.shuffle -inf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) " + "(i8x16.shuffle -inf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 " "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") (assert_malformed (module quote "(func (result v128) " - "(i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 inf) " + "(i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 inf " "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") (assert_malformed (module quote "(func (result v128) " - "(i8x16.shuffle nan 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) " + "(i8x16.shuffle nan 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 " "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") diff --git a/test/core/simd/simd_load.wast b/test/core/simd/simd_load.wast index 4b2edc160..2fddd3019 100644 --- a/test/core/simd/simd_load.wast +++ b/test/core/simd/simd_load.wast @@ -1,4 +1,4 @@ -;; v128.load operater with normal argument (e.g. (i8x16, i16x8 i32x4)) +;; v128.load operator with normal argument (e.g. (i8x16, i16x8 i32x4)) (module (memory 1) @@ -13,7 +13,7 @@ (assert_return (invoke "v128.load") (v128.const i32x4 0x03020100 0x07060504 0x0b0a0908 0x0f0e0d0c)) -;; v128.load operater as the argument of other SIMD instructions +;; v128.load operator as the argument of other SIMD instructions (module (memory 1) (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\00\01\02\03") @@ -185,4 +185,4 @@ (assert_invalid (module (memory 1) (func (drop (v128.load)))) "type mismatch" -) \ No newline at end of file +) diff --git a/test/core/simd/simd_store.wast b/test/core/simd/simd_store.wast index 50349c41b..7bba053d8 100644 --- a/test/core/simd/simd_store.wast +++ b/test/core/simd/simd_store.wast @@ -1,4 +1,4 @@ -;; v128.store operater with normal argument (e.g. (i8x16, i16x8, i32x4, f32x4)) +;; v128.store operator with normal argument (e.g. (i8x16, i16x8, i32x4, f32x4)) (module (memory 1) diff --git a/test/core/stack-switching/cont.wast b/test/core/stack-switching/cont.wast new file mode 100644 index 000000000..78903963d --- /dev/null +++ b/test/core/stack-switching/cont.wast @@ -0,0 +1,1216 @@ +;; Unhandled tags & guards + +(module + (tag $exn) + (tag $e1) + (tag $e2) + + (type $f1 (func)) + (type $k1 (cont $f1)) + + (func $f1 (export "unhandled-1") + (suspend $e1) + ) + + (func (export "unhandled-2") + (resume $k1 (cont.new $k1 (ref.func $f1))) + ) + + (func (export "unhandled-3") + (block $h (result (ref $k1)) + (resume $k1 (on $e2 $h) (cont.new $k1 (ref.func $f1))) + (unreachable) + ) + (drop) + ) + + (func (export "handled") + (block $h (result (ref $k1)) + (resume $k1 (on $e1 $h) (cont.new $k1 (ref.func $f1))) + (unreachable) + ) + (drop) + ) + + (elem declare func $f2) + (func $f2 + (throw $exn) + ) + + (func (export "uncaught-1") + (block $h (result (ref $k1)) + (resume $k1 (on $e1 $h) (cont.new $k1 (ref.func $f2))) + (unreachable) + ) + (drop) + ) + + (func (export "uncaught-2") + (block $h (result (ref $k1)) + (resume $k1 (on $e1 $h) (cont.new $k1 (ref.func $f1))) + (unreachable) + ) + (resume_throw $k1 $exn) + ) + + (elem declare func $f3) + (func $f3 + (call $f4) + ) + (func $f4 + (suspend $e1) + ) + + (func (export "uncaught-3") + (block $h (result (ref $k1)) + (resume $k1 (on $e1 $h) (cont.new $k1 (ref.func $f3))) + (unreachable) + ) + (resume_throw $k1 $exn) + ) + + (elem declare func $r0 $r1) + (func $r0) + (func $r1 (suspend $e1) (suspend $e1)) + + (func $nl1 (param $k (ref $k1)) + (resume $k1 (local.get $k)) + (resume $k1 (local.get $k)) + ) + (func $nl2 (param $k (ref $k1)) + (block $h (result (ref $k1)) + (resume $k1 (on $e1 $h) (local.get $k)) + (unreachable) + ) + (resume $k1 (local.get $k)) + (unreachable) + ) + (func $nl3 (param $k (ref $k1)) + (local $k' (ref null $k1)) + (block $h1 (result (ref $k1)) + (resume $k1 (on $e1 $h1) (local.get $k)) + (unreachable) + ) + (local.set $k') + (block $h2 (result (ref $k1)) + (resume $k1 (on $e1 $h2) (local.get $k')) + (unreachable) + ) + (resume $k1 (local.get $k')) + (unreachable) + ) + (func $nl4 (param $k (ref $k1)) + (drop (cont.bind $k1 $k1 (local.get $k))) + (resume $k1 (local.get $k)) + ) + + (func (export "non-linear-1") + (call $nl1 (cont.new $k1 (ref.func $r0))) + ) + (func (export "non-linear-2") + (call $nl2 (cont.new $k1 (ref.func $r1))) + ) + (func (export "non-linear-3") + (call $nl3 (cont.new $k1 (ref.func $r1))) + ) + (func (export "non-linear-4") + (call $nl4 (cont.new $k1 (ref.func $r1))) + ) + + (func (export "null-resume") + (resume $k1 + (ref.null $k1) + ) + ) + + (func (export "null-new") + (cont.new $k1 + (ref.null $f1) + ) + (drop) + ) + + (func (export "null-bind") + (drop + (cont.bind $k1 $k1 + (ref.null $k1) + ) + ) + ) +) + +(assert_suspension (invoke "unhandled-1") "unhandled") +(assert_suspension (invoke "unhandled-2") "unhandled") +(assert_suspension (invoke "unhandled-3") "unhandled") +(assert_return (invoke "handled")) + +(assert_exception (invoke "uncaught-1")) +(assert_exception (invoke "uncaught-2")) +(assert_exception (invoke "uncaught-3")) + +(assert_trap (invoke "non-linear-1") "continuation already consumed") +(assert_trap (invoke "non-linear-2") "continuation already consumed") +(assert_trap (invoke "non-linear-3") "continuation already consumed") +(assert_trap (invoke "non-linear-4") "continuation already consumed") + +(assert_trap (invoke "null-resume") "null continuation reference") +(assert_trap (invoke "null-new") "null function reference") +(assert_trap (invoke "null-bind") "null continuation reference") + +(assert_invalid + (module + (type $ft (func)) + (func + (cont.new $ft (ref.null $ft)) + (drop))) + "non-continuation type 0") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (func + (resume $ft (ref.null $ct)) + (unreachable))) + "non-continuation type 0") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $exn) + (func + (resume_throw $ft $exn (ref.null $ct)) + (unreachable))) + "non-continuation type 0") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (func + (cont.bind $ft $ct (ref.null $ct)) + (unreachable))) + "non-continuation type 0") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (func + (cont.bind $ct $ft (ref.null $ct)) + (unreachable))) + "non-continuation type 0") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $foo) + (func + (block $on_foo (result (ref $ft)) + (resume $ct (on $foo $on_foo) (ref.null $ct)) + (unreachable) + ) + (drop))) + "non-continuation type 0") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $foo) + (func + (block $on_foo (result (ref $ct) (ref $ft)) + (resume $ct (on $foo $on_foo) (ref.null $ct)) + (unreachable) + ) + (drop) + (drop))) + "non-continuation type 0") + +(assert_invalid + (module + (type $ct (cont $ct))) + "non-function type 0") + +(assert_invalid + (module + (rec + (type $s0 (struct (field (ref 0) (ref 1) (ref $s0) (ref $s1)))) + (type $s1 (struct (field (ref 0) (ref 1) (ref $s0) (ref $s1)))) + ) + (type $ct (cont $s0))) + "non-function type 0") + +(module + (rec + (type $f1 (func (param (ref $f2)))) + (type $f2 (func (param (ref $f1)))) + ) + (type $c1 (cont $f1)) + (type $c2 (cont $f2)) +) + +(assert_invalid + (module + (rec + (type $fA (func)) + (type $fB (func)) + (type $cont (cont $fA)) + ) + (elem declare func $b) + (func $a + (drop + (cont.new $cont ;; expects a ref of $fA, not $fB + (ref.func $b) + ) + ) + ) + (func $b (type $fB) + ) + ) + "type mismatch") + + +;; Test resume_throw used on the very first execution of a continuation (so the +;; code in the continuation function is never reached). +(module + (tag $exn) + + (type $f (func)) + (type $k (cont $f)) + + (func $never + (unreachable) + ) + + (func (export "resume_throw-never") + (block $handle + (try_table (catch $exn $handle) + (resume_throw $k $exn + (cont.new $k (ref.func $never)) + ) + ) + ) + ) + + (elem declare func $never) +) + +(assert_return (invoke "resume_throw-never")) + +;; Simple state example + +(module $state + (tag $get (result i32)) + (tag $set (param i32) (result i32)) + + (type $f (func (param i32) (result i32))) + (type $k (cont $f)) + + (func $runner (param $s i32) (param $k (ref $k)) (result i32) + (loop $loop + (block $on_get (result (ref $k)) + (block $on_set (result i32 (ref $k)) + (resume $k (on $get $on_get) (on $set $on_set) + (local.get $s) (local.get $k) + ) + (return) + ) + ;; on set + (local.set $k) + (local.set $s) + (br $loop) + ) + ;; on get + (local.set $k) + (br $loop) + ) + (unreachable) + ) + + (func $f (param i32) (result i32) + (drop (suspend $set (i32.const 7))) + (i32.add + (suspend $get) + (i32.mul + (i32.const 2) + (i32.add + (suspend $set (i32.const 3)) + (suspend $get) + ) + ) + ) + ) + + (elem declare func $f) + (func (export "run") (result i32) + (call $runner (i32.const 0) (cont.new $k (ref.func $f))) + ) +) + +(assert_return (invoke "run") (i32.const 19)) + + +;; Simple generator example + +(module $generator + (type $gen (func (param i64))) + (type $geny (func (param i32))) + (type $cont0 (cont $gen)) + (type $cont (cont $geny)) + + (tag $yield (param i64) (result i32)) + + ;; Hook for logging purposes + (global $hook (export "hook") (mut (ref $gen)) (ref.func $dummy)) + (func $dummy (param i64)) + + (func $gen (export "start") (param $i i64) + (loop $l + (br_if 1 (suspend $yield (local.get $i))) + (call_ref $gen (local.get $i) (global.get $hook)) + (local.set $i (i64.add (local.get $i) (i64.const 1))) + (br $l) + ) + ) + + (elem declare func $gen) + + (func (export "sum") (param $i i64) (param $j i64) (result i64) + (local $sum i64) + (local $n i64) + (local $k (ref null $cont)) + (local.get $i) + (cont.new $cont0 (ref.func $gen)) + (block $on_first_yield (param i64 (ref $cont0)) (result i64 (ref $cont)) + (resume $cont0 (on $yield $on_first_yield)) + (unreachable) + ) + (loop $on_yield (param i64) (param (ref $cont)) + (local.set $k) + (local.set $n) + (local.set $sum (i64.add (local.get $sum) (local.get $n))) + (i64.eq (local.get $n) (local.get $j)) + (local.get $k) + (resume $cont (on $yield $on_yield)) + ) + (return (local.get $sum)) + ) +) + +(register "generator") + +(assert_return (invoke "sum" (i64.const 0) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "sum" (i64.const 2) (i64.const 2)) (i64.const 2)) +(assert_return (invoke "sum" (i64.const 0) (i64.const 3)) (i64.const 6)) +(assert_return (invoke "sum" (i64.const 1) (i64.const 10)) (i64.const 55)) +(assert_return (invoke "sum" (i64.const 100) (i64.const 2000)) (i64.const 1_996_050)) + + +;; Simple scheduler example + +(module $scheduler + (type $proc (func)) + (type $cont (cont $proc)) + + (tag $yield (export "yield")) + (tag $spawn (export "spawn") (param (ref $cont))) + + ;; Table as simple queue (keeping it simple, no ring buffer) + (table $queue 0 (ref null $cont)) + (global $qdelta i32 (i32.const 10)) + (global $qback (mut i32) (i32.const 0)) + (global $qfront (mut i32) (i32.const 0)) + + (func $queue-empty (result i32) + (i32.eq (global.get $qfront) (global.get $qback)) + ) + + (func $dequeue (result (ref null $cont)) + (local $i i32) + (if (call $queue-empty) + (then (return (ref.null $cont))) + ) + (local.set $i (global.get $qfront)) + (global.set $qfront (i32.add (local.get $i) (i32.const 1))) + (table.get $queue (local.get $i)) + ) + + (func $enqueue (param $k (ref $cont)) + ;; Check if queue is full + (if (i32.eq (global.get $qback) (table.size $queue)) + (then + ;; Check if there is enough space in the front to compact + (if (i32.lt_u (global.get $qfront) (global.get $qdelta)) + (then + ;; Space is below threshold, grow table instead + (drop (table.grow $queue (ref.null $cont) (global.get $qdelta))) + ) + (else + ;; Enough space, move entries up to head of table + (global.set $qback (i32.sub (global.get $qback) (global.get $qfront))) + (table.copy $queue $queue + (i32.const 0) ;; dest = new front = 0 + (global.get $qfront) ;; src = old front + (global.get $qback) ;; len = new back = old back - old front + ) + (table.fill $queue ;; null out old entries to avoid leaks + (global.get $qback) ;; start = new back + (ref.null $cont) ;; init value + (global.get $qfront) ;; len = old front = old front - new front + ) + (global.set $qfront (i32.const 0)) + ) + ) + ) + ) + (table.set $queue (global.get $qback) (local.get $k)) + (global.set $qback (i32.add (global.get $qback) (i32.const 1))) + ) + + (func $scheduler (export "scheduler") (param $main (ref $cont)) + (call $enqueue (local.get $main)) + (loop $l + (if (call $queue-empty) (then (return))) + (block $on_yield (result (ref $cont)) + (block $on_spawn (result (ref $cont) (ref $cont)) + (resume $cont (on $yield $on_yield) (on $spawn $on_spawn) + (call $dequeue) + ) + (br $l) ;; thread terminated + ) + ;; on $spawn, proc and cont on stack + (call $enqueue) ;; continuation of old thread + (call $enqueue) ;; new thread + (br $l) + ) + ;; on $yield, cont on stack + (call $enqueue) + (br $l) + ) + ) +) + +(register "scheduler") + +(module + (type $proc (func)) + (type $pproc (func (param i32))) ;; parameterised proc + (type $cont (cont $proc)) + (type $pcont (cont $pproc)) ;; parameterised continuation proc + (tag $yield (import "scheduler" "yield")) + (tag $spawn (import "scheduler" "spawn") (param (ref $cont))) + (func $scheduler (import "scheduler" "scheduler") (param $main (ref $cont))) + + (func $log (import "spectest" "print_i32") (param i32)) + + (global $width (mut i32) (i32.const 0)) + (global $depth (mut i32) (i32.const 0)) + + (elem declare func $main $thread1 $thread2 $thread3) + + (func $main + (call $log (i32.const 0)) + (suspend $spawn (cont.new $cont (ref.func $thread1))) + (call $log (i32.const 1)) + (suspend $spawn (cont.bind $pcont $cont (global.get $depth) (cont.new $pcont (ref.func $thread2)))) + (call $log (i32.const 2)) + (suspend $spawn (cont.new $cont (ref.func $thread3))) + (call $log (i32.const 3)) + ) + + (func $thread1 + (call $log (i32.const 10)) + (suspend $yield) + (call $log (i32.const 11)) + (suspend $yield) + (call $log (i32.const 12)) + (suspend $yield) + (call $log (i32.const 13)) + ) + + (func $thread2 (param $d i32) + (local $w i32) + (local.set $w (global.get $width)) + (call $log (i32.const 20)) + (br_if 0 (i32.eqz (local.get $d))) + (call $log (i32.const 21)) + (loop $l + (if (local.get $w) + (then + (call $log (i32.const 22)) + (suspend $yield) + (call $log (i32.const 23)) + (suspend $spawn + (cont.bind $pcont $cont + (i32.sub (local.get $d) (i32.const 1)) + (cont.new $pcont (ref.func $thread2)) + ) + ) + (call $log (i32.const 24)) + (local.set $w (i32.sub (local.get $w) (i32.const 1))) + (br $l) + ) + ) + ) + (call $log (i32.const 25)) + ) + + (func $thread3 + (call $log (i32.const 30)) + (suspend $yield) + (call $log (i32.const 31)) + (suspend $yield) + (call $log (i32.const 32)) + ) + + (func (export "run") (param $width i32) (param $depth i32) + (global.set $depth (local.get $depth)) + (global.set $width (local.get $width)) + (call $log (i32.const -1)) + (call $scheduler (cont.new $cont (ref.func $main))) + (call $log (i32.const -2)) + ) +) + +(assert_return (invoke "run" (i32.const 0) (i32.const 0))) +(assert_return (invoke "run" (i32.const 0) (i32.const 1))) +(assert_return (invoke "run" (i32.const 1) (i32.const 0))) +(assert_return (invoke "run" (i32.const 1) (i32.const 1))) +(assert_return (invoke "run" (i32.const 3) (i32.const 4))) + + +;; Nested example: generator in a thread + +(module $concurrent_generator + (func $log (import "spectest" "print_i64") (param i64)) + + (tag $syield (import "scheduler" "yield")) + (tag $spawn (import "scheduler" "spawn") (param (ref $cont))) + (func $scheduler (import "scheduler" "scheduler") (param $main (ref $cont))) + + (type $ghook (func (param i64))) + (func $gsum (import "generator" "sum") (param i64 i64) (result i64)) + (global $ghook (import "generator" "hook") (mut (ref $ghook))) + + (global $result (mut i64) (i64.const 0)) + (global $done (mut i32) (i32.const 0)) + + (elem declare func $main $bg-thread $syield) + + (func $syield (param $i i64) + (call $log (local.get $i)) + (suspend $syield) + ) + + (func $bg-thread + (call $log (i64.const -10)) + (loop $l + (call $log (i64.const -11)) + (suspend $syield) + (br_if $l (i32.eqz (global.get $done))) + ) + (call $log (i64.const -12)) + ) + + (func $main (param $i i64) (param $j i64) + (suspend $spawn (cont.new $cont (ref.func $bg-thread))) + (global.set $ghook (ref.func $syield)) + (global.set $result (call $gsum (local.get $i) (local.get $j))) + (global.set $done (i32.const 1)) + ) + + (type $proc (func)) + (type $pproc (func (param i64 i64))) + (type $cont (cont $proc)) + (type $pcont (cont $pproc)) + (func (export "sum") (param $i i64) (param $j i64) (result i64) + (call $log (i64.const -1)) + (call $scheduler + (cont.bind $pcont $cont (local.get $i) (local.get $j) (cont.new $pcont (ref.func $main))) + ) + (call $log (i64.const -2)) + (global.get $result) + ) +) + +(assert_return (invoke "sum" (i64.const 10) (i64.const 20)) (i64.const 165)) + + +;; cont.bind + +(module + (type $f2 (func (param i32 i32) (result i32 i32 i32 i32 i32 i32))) + (type $f4 (func (param i32 i32 i32 i32) (result i32 i32 i32 i32 i32 i32))) + (type $f6 (func (param i32 i32 i32 i32 i32 i32) (result i32 i32 i32 i32 i32 i32))) + + (type $k2 (cont $f2)) + (type $k4 (cont $f4)) + (type $k6 (cont $f6)) + + (elem declare func $f) + (func $f (param i32 i32 i32 i32 i32 i32) (result i32 i32 i32 i32 i32 i32) + (local.get 0) (local.get 1) (local.get 2) + (local.get 3) (local.get 4) (local.get 5) + ) + + (func (export "run") (result i32 i32 i32 i32 i32 i32) + (local $k6 (ref null $k6)) + (local $k4 (ref null $k4)) + (local $k2 (ref null $k2)) + (local.set $k6 (cont.new $k6 (ref.func $f))) + (local.set $k4 (cont.bind $k6 $k4 (i32.const 1) (i32.const 2) (local.get $k6))) + (local.set $k2 (cont.bind $k4 $k2 (i32.const 3) (i32.const 4) (local.get $k4))) + (resume $k2 (i32.const 5) (i32.const 6) (local.get $k2)) + ) +) + +(assert_return (invoke "run") + (i32.const 1) (i32.const 2) (i32.const 3) + (i32.const 4) (i32.const 5) (i32.const 6) +) + + +(module + (tag $e (result i32 i32 i32 i32 i32 i32)) + + (type $f0 (func (result i32 i32 i32 i32 i32 i32 i32))) + (type $f2 (func (param i32 i32) (result i32 i32 i32 i32 i32 i32 i32))) + (type $f4 (func (param i32 i32 i32 i32) (result i32 i32 i32 i32 i32 i32 i32))) + (type $f6 (func (param i32 i32 i32 i32 i32 i32) (result i32 i32 i32 i32 i32 i32 i32))) + + (type $k0 (cont $f0)) + (type $k2 (cont $f2)) + (type $k4 (cont $f4)) + (type $k6 (cont $f6)) + + (elem declare func $f) + (func $f (result i32 i32 i32 i32 i32 i32 i32) + (i32.const 0) (suspend $e) + ) + + (func (export "run") (result i32 i32 i32 i32 i32 i32 i32) + (local $k6 (ref null $k6)) + (local $k4 (ref null $k4)) + (local $k2 (ref null $k2)) + (block $l (result (ref $k6)) + (resume $k0 (on $e $l) (cont.new $k0 (ref.func $f))) + (unreachable) + ) + (local.set $k6) + (local.set $k4 (cont.bind $k6 $k4 (i32.const 1) (i32.const 2) (local.get $k6))) + (local.set $k2 (cont.bind $k4 $k2 (i32.const 3) (i32.const 4) (local.get $k4))) + (resume $k2 (i32.const 5) (i32.const 6) (local.get $k2)) + ) +) + +(assert_return (invoke "run") + (i32.const 0) (i32.const 1) (i32.const 2) (i32.const 3) + (i32.const 4) (i32.const 5) (i32.const 6) +) + +;; Subtyping +(module + (type $ft1 (func (param i32))) + (type $ct1 (sub (cont $ft1))) + + (type $ft0 (func)) + (type $ct0 (sub (cont $ft0))) + + (func $test (param $x (ref $ct1)) + (i32.const 123) + (local.get $x) + (cont.bind $ct1 $ct0) + (drop) + ) +) + +(module + (type $f1 (sub (func (result anyref)))) + (type $f2 (sub $f1 (func (result eqref)))) + (type $c1 (sub (cont $f1))) + (type $c2 (sub $c1 (cont $f2))) +) + +;; Globals +(module + (type $ft (func)) + (type $ct (cont $ft)) + + (global $k (mut (ref null $ct)) (ref.null $ct)) + (global $g (ref null $ct) (ref.null $ct)) + + (func $f) + (elem declare func $f) + + (func (export "set-global") + (global.set $k (cont.new $ct (ref.func $f)))) +) +(assert_return (invoke "set-global")) + +;; Switch +(module + (rec + (type $ft (func (param (ref null $ct)))) + (type $ct (cont $ft))) + + (func $print-i32 (import "spectest" "print_i32") (param i32)) + + (global $fi (mut i32) (i32.const 0)) + (global $gi (mut i32) (i32.const 1)) + + (tag $swap) + + (func $init (export "init") (result i32) + (resume $ct (on $swap switch) + (cont.new $ct (ref.func $g)) + (cont.new $ct (ref.func $f))) + (return (i32.const 42))) + (func $f (type $ft) + (local $nextk (ref null $ct)) + (local.set $nextk (local.get 0)) + (call $print-i32 (global.get $fi)) + (switch $ct $swap (local.get $nextk)) + (local.set $nextk) + (call $print-i32 (global.get $fi)) + (switch $ct $swap (local.get $nextk)) + (drop)) + (func $g (type $ft) + (local $nextk (ref null $ct)) + (local.set $nextk (local.get 0)) + (call $print-i32 (global.get $gi)) + (switch $ct $swap (local.get $nextk)) + (local.set $nextk) + (call $print-i32 (global.get $gi))) + (elem declare func $f $g) +) +(assert_return (invoke "init") (i32.const 42)) + +(module + (rec + (type $ft (func (param i32) (param (ref null $ct)) (result i32))) + (type $ct (cont $ft))) + + (func $print-i32 (import "spectest" "print_i32") (param i32)) + + (tag $swap (result i32)) + + (func $init (export "init") (result i32) + (resume $ct (on $swap switch) + (i32.const 1) + (cont.new $ct (ref.func $g)) + (cont.new $ct (ref.func $f)))) + (func $f (type $ft) + (local $i i32) + (local $nextk (ref null $ct)) + (local.set $i (local.get 0)) + (local.set $nextk (local.get 1)) + (call $print-i32 (local.get $i)) + (switch $ct $swap (i32.add (i32.const 1) (local.get $i)) (local.get $nextk)) + (local.set $nextk) + (local.set $i) + (call $print-i32 (local.get $i)) + (switch $ct $swap (i32.add (i32.const 1) (local.get $i)) (local.get $nextk)) + (unreachable)) + (func $g (type $ft) + (local $i i32) + (local $nextk (ref null $ct)) + (local.set $i (local.get 0)) + (local.set $nextk (local.get 1)) + (call $print-i32 (local.get $i)) + (switch $ct $swap (i32.add (i32.const 1) (local.get $i)) (local.get $nextk)) + (local.set $nextk) + (local.set $i) + (call $print-i32 (local.get $i)) + (return (local.get $i))) + (elem declare func $f $g) +) +(assert_return (invoke "init") (i32.const 4)) + + +(assert_invalid + (module + (rec + (type $ft (func (param (ref null $ct)))) + (type $ct (cont $ft))) + (type $ft2 (func)) + (type $ct2 (cont $ft2)) + + (tag $swap) + (func $f (type $ft) + (switch $ct $swap (cont.new $ct2 (ref.null $ft2))) + (drop))) + "type mismatch") + +(assert_invalid + (module + (rec + (type $ft (func (param i32) (param (ref null $ct)))) + (type $ct (cont $ft))) + + (tag $swap) + (func $f (type $ft) + (switch $ct $swap (i64.const 0) (local.get 1)) + (drop) + (drop))) + "type mismatch") + +(module + (type $ft1 (func)) + (type $ct1 (cont $ft1)) + (rec + (type $ft2 (func (param (ref null $ct2)))) + (type $ct2 (cont $ft2))) + + (tag $t) + + (func $suspend (type $ft2) + (suspend $t)) + + (func $switch (type $ft2) + (switch $ct2 $t (local.get 0)) + (drop)) + + (func (export "unhandled-suspend-t") + (resume $ct2 (on $t switch) + (cont.new $ct2 (ref.func $suspend)) + (cont.new $ct2 (ref.func $suspend)))) + (func (export "unhandled-switch-t") + (block $l (result (ref $ct1)) + (resume $ct2 (on $t $l) + (cont.new $ct2 (ref.func $switch)) + (cont.new $ct2 (ref.func $switch))) + (unreachable) + ) + (unreachable)) + + (elem declare func $suspend $switch) +) +(assert_suspension (invoke "unhandled-suspend-t") "unhandled tag") +(assert_suspension (invoke "unhandled-switch-t") "unhandled tag") + +(module + (rec + (type $ft (func (param (ref null $ct)))) + (type $ct (cont $ft))) + + (tag $t) + + (func + (cont.new $ct (ref.null $ft)) + (unreachable)) + (func + (cont.bind $ct $ct (ref.null $ct)) + (unreachable)) + (func + (resume $ct (ref.null $ct) (ref.null $ct)) + (unreachable)) + (func + (resume_throw $ct $t (ref.null $ct)) + (unreachable)) + (func + (switch $ct $t (ref.null $ct)) + (unreachable)) +) + +(module $co2 + (type $task (func (result i32))) ;; type alias task = [] -> [] + (type $ct (cont $task)) ;; type alias ct = $task + (tag $pause (export "pause")) ;; pause : [] -> [] + (tag $cancel (export "cancel")) ;; cancel : [] -> [] + ;; run : [(ref $task) (ref $task)] -> [] + ;; implements a 'seesaw' (c.f. Ganz et al. (ICFP@99)) + (func $run (export "seesaw") (param $up (ref $ct)) (param $down (ref $ct)) (result i32) + (local $result i32) + ;; run $up + (loop $run_next (result i32) + (block $on_pause (result (ref $ct)) + (resume $ct (on $pause $on_pause) + (local.get $up)) + ;; $up finished, store its result + (local.set $result) + ;; next cancel $down + (block $on_cancel + (try_table (catch $cancel $on_cancel) + ;; inject the cancel exception into $down + (resume_throw $ct $cancel (local.get $down)) + (drop) ;; drop the return value if it handled $cancel + ;; itself and returned normally... + ) + ) ;; ... otherwise catch $cancel and return $up's result. + (return (local.get $result)) + ) ;; on_pause clause, stack type: [(cont $ct)] + (local.set $up) + ;; swap $up and $down + (local.get $down) + (local.set $down (local.get $up)) + (local.set $up) + (br $run_next) + ) + ) +) +(register "co2") + +(module $client + (type $task-0 (func (param i32) (result i32))) + (type $ct-0 (cont $task-0)) + (type $task (func (result i32))) + (type $ct (cont $task)) + + (func $seesaw (import "co2" "seesaw") (param (ref $ct)) (param (ref $ct)) (result i32)) + (func $print-i32 (import "spectest" "print_i32") (param i32)) + (tag $pause (import "co2" "pause")) + + (func $even (param $niter i32) (result i32) + (local $next i32) ;; zero initialised. + (local $i i32) + (loop $print-next + (call $print-i32 (local.get $next)) + (suspend $pause) + (local.set $next (i32.add (local.get $next) (i32.const 2))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br_if $print-next (i32.lt_u (local.get $i) (local.get $niter))) + ) + (local.get $next) + ) + (func $odd (param $niter i32) (result i32) + (local $next i32) ;; zero initialised. + (local $i i32) + (local.set $next (i32.const 1)) + (loop $print-next + (call $print-i32 (local.get $next)) + (suspend $pause) + (local.set $next (i32.add (local.get $next) (i32.const 2))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br_if $print-next (i32.lt_u (local.get $i) (local.get $niter))) + ) + (local.get $next) + ) + + (func (export "main") (result i32) + (call $seesaw + (cont.bind $ct-0 $ct + (i32.const 5) (cont.new $ct-0 (ref.func $even))) + (cont.bind $ct-0 $ct + (i32.const 5) (cont.new $ct-0 (ref.func $odd))))) + + (elem declare func $even $odd) +) +(assert_return (invoke "main") (i32.const 10)) + +(module + (type $f1 (func (result i32))) + (type $c1 (cont $f1)) + (type $f2 (func (param (ref null $c1)) (result i32))) + (type $c2 (cont $f2)) + (type $f3 (func (param (ref null $c2)) (result i32))) + (type $c3 (cont $f3)) + (tag $e (result i32)) + + (func $fn_1 (param (ref null $c2)) (result i32) + (local.get 0) + (switch $c2 $e) + (i32.const 24) + ) + (elem declare func $fn_1) + + (func $fn_2 (result i32) + (cont.new $c3 (ref.func $fn_1)) + (switch $c3 $e) + (drop) + (i32.const -1) + ) + (elem declare func $fn_2) + + (func (export "main") (result i32) + (cont.new $c1 (ref.func $fn_2)) + (resume $c1 (on $e switch)) + ) +) + +(assert_return (invoke "main") (i32.const -1)) + +;; Syntax: check unfolded forms +(module + (type $ft (func)) + (type $ct (cont $ft)) + (rec + (type $ft2 (func (param (ref null $ct2)))) + (type $ct2 (cont $ft2))) + + (tag $yield (param i32)) + (tag $swap) + + ;; Check cont.new + (func (result (ref $ct)) + ref.null $ft + block (param (ref null $ft)) (result (ref $ct)) + cont.new $ct + end + ) + ;; Check cont.bind + (func (param (ref $ct)) (result (ref $ct)) + local.get 0 + block (param (ref $ct)) (result (ref $ct)) + cont.bind $ct $ct + end + ) + ;; Check suspend + (func + block + suspend $swap + end + ) + ;; Check resume + (func (param $k (ref $ct)) (result i32) + (local.get $k) + block $on_yield (param (ref $ct)) (result i32 (ref $ct)) + resume $ct (on $yield $on_yield) + i32.const 42 + return + end + local.set $k + ) + ;; Check resume_throw + (func (param $k (ref $ct)) (result i32) + block $on_yield (result i32 (ref $ct)) + i32.const 42 + local.get $k + resume_throw $ct $yield + i32.const 42 + return + end + local.set $k + ) + ;; Check switch + (func (param $k (ref $ct2)) + local.get $k + block (param (ref $ct2)) (result (ref null $ct2)) + switch $ct2 $swap + end + drop + ) +) + +;; Syntax: check instructions in tail position in unfolded form +(module + (type $ft (func)) + (type $ct (cont $ft)) + (rec + (type $ft2 (func (param (ref null $ct2)))) + (type $ct2 (cont $ft2))) + + (tag $yield (param i32)) + (tag $swap) + + ;; Check cont.new + (func (result (ref $ct)) + ref.null $ft + cont.new $ct + ) + ;; Check cont.bind + (func (param (ref $ct)) (result (ref $ct)) + local.get 0 + cont.bind $ct $ct + ) + + ;; Check resume + (func (;2;) (param $k (ref $ct)) + local.get $k + resume $ct + ) + ;; Check resume_throw + (func (param $k (ref $ct)) + i32.const 42 + local.get $k + resume_throw $ct $yield + ) + ;; Check switch + (func (param $k (ref $ct2)) (result (ref null $ct2)) + local.get $k + switch $ct2 $swap + ) + ;; Check suspend + (func + suspend $swap + ) +) + +(module + (type $ft0 (func)) + (type $ct0 (cont $ft0)) + + (type $ft1 (func (param (ref $ct0)))) + (type $ct1 (cont $ft1)) + + (tag $t) + + (func $f + (cont.new $ct1 (ref.func $g)) + (switch $ct1 $t) + ) + (elem declare func $f) + + (func $g (param (ref $ct0))) + (elem declare func $g) + + (func $entry + (cont.new $ct0 (ref.func $f)) + (resume $ct0 (on $t switch)) + ) +) + +(assert_invalid + (module + (rec + (type $ft (func (param (ref $ct)))) + (type $ct (cont $ft))) + (tag $t (param i32)) + + (func (param $k (ref $ct)) + (switch $ct $t))) + "type mismatch in switch tag") + +;; Synthesized from https://github.com/WebAssembly/stack-switching/issues/117 +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $t) + + (func + (block $on_t (result (ref cont)) + (resume $ct (on $t $on_t) (cont.new $ct (ref.null $ft))) + (unreachable) + ) + (drop) + )) + "type mismatch: instruction requires concrete continuation reference type but label has [(ref cont)]") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $t) + + (func + (block $on_t (result (ref nocont)) + (resume $ct (on $t $on_t) (cont.new $ct (ref.null $ft))) + (unreachable) + ) + (drop) + )) + "type mismatch: instruction requires concrete continuation reference type but label has [(ref nocont)]") + +;; https://github.com/WebAssembly/stack-switching/issues/117#issuecomment-2908974084 +(module + (type $f (func)) + (type $c (sub (cont $f))) + (tag $e) + (func (param $c (ref $c)) + (local.get $c) + (resume $c) + ) +) diff --git a/test/core/stack-switching/resume_throw.wast b/test/core/stack-switching/resume_throw.wast new file mode 100644 index 000000000..2b19f3671 --- /dev/null +++ b/test/core/stack-switching/resume_throw.wast @@ -0,0 +1,312 @@ +;; Tests for resume_throw + +;; Test resume_throw on a continuation that is never resumed. +(module + (tag $exn) + + (type $f (func)) + (type $k (cont $f)) + + (func $never (unreachable)) + (elem declare func $never) + + (func (export "throw_never") + (block $h + (try_table (catch $exn $h) + (cont.new $k (ref.func $never)) + (resume_throw $k $exn) + (unreachable) + ) + ) + ) +) +(assert_return (invoke "throw_never")) + +;; Test resume_throw with a value type argument. +(module + (tag $exn_i32 (param i32)) + + (type $f (func)) + (type $k (cont $f)) + + (func $never (unreachable)) + (elem declare func $never) + + (func (export "throw_never_i32") (result i32) + (block $h (result i32) + (try_table (result i32) (catch $exn_i32 $h) + (i32.const 42) + (cont.new $k (ref.func $never)) + (resume_throw $k $exn_i32) + (unreachable) + ) + ) + ) +) +(assert_return (invoke "throw_never_i32") (i32.const 42)) + +;; Test resume_throw with a reference type argument. +(module + (tag $exn_ref (param externref)) + + (type $f (func)) + (type $k (cont $f)) + + (func $never (unreachable)) + (elem declare func $never) + + (func (export "throw_never_ref") (param $val externref) (result externref) + (block $h (result externref) + (try_table (result externref) (catch $exn_ref $h) + (local.get $val) + (cont.new $k (ref.func $never)) + (resume_throw $k $exn_ref) + (unreachable) + ) + ) + ) +) +(assert_return (invoke "throw_never_ref" (ref.extern 1)) (ref.extern 1)) + +;; Test resume_throw where the continuation handles the exception. +(module + (tag $exn) + (tag $e1) + + (type $f (func)) + (type $k (cont $f)) + + (func $handler + (block $h + (try_table (catch $exn $h) + (suspend $e1) + ) + ) + ) + (elem declare func $handler) + + (func (export "throw_handled") + (block $h (result (ref $k)) + (resume $k (on $e1 $h) (cont.new $k (ref.func $handler))) + (unreachable) + ) + (resume_throw $k $exn) + ) +) +(assert_return (invoke "throw_handled")) + +;; Test resume_throw where the continuation does not handle the exception. +(module + (tag $exn) + (tag $e1) + + (type $f (func)) + (type $k (cont $f)) + + (func $no_handler + (suspend $e1) + ) + (elem declare func $no_handler) + + (func (export "throw_unhandled") + (block $h (result (ref $k)) + (resume $k (on $e1 $h) (cont.new $k (ref.func $no_handler))) + (unreachable) + ) + (resume_throw $k $exn) + ) +) +(assert_exception (invoke "throw_unhandled")) + +;; Test resume_throw on a consumed continuation. +(module + (tag $exn) + + (type $f (func)) + (type $k (cont $f)) + + (func $f1) + (elem declare func $f1) + + (func (export "throw_consumed") + (local $k_ref (ref $k)) + (local.set $k_ref (cont.new $k (ref.func $f1))) + (resume $k (local.get $k_ref)) ;; consume it + (resume_throw $k $exn (local.get $k_ref)) ;; should trap + ) +) +(assert_trap (invoke "throw_consumed") "continuation already consumed") + +;; Test resume_throw on a null continuation reference. +(module + (tag $exn) + (type $f (func)) + (type $k (cont $f)) + (func (export "throw_null") + (resume_throw $k $exn (ref.null $k)) + ) +) +(assert_trap (invoke "throw_null") "null continuation reference") + +;; Test resume_throw_ref where the continuation handles the exception. +(module + (tag $e0 (param i32)) + (tag $yield) + + (type $f (func (result i32))) + (type $k (cont $f)) + + (func (export "throw_handled_ref") (result i32) + (local $k_ref (ref $k)) + (local.set $k_ref (cont.new $k (ref.func $yield42))) + + (block $y (result (ref $k)) + (resume $k (on $yield $y) + (local.get $k_ref)) + (unreachable)) + (local.set $k_ref) + + (block $h (result i32 exnref) + (try_table (catch_ref $e0 $h) + (i32.const 42) + (throw $e0)) + (unreachable) + ) + + (resume_throw_ref $k (local.get $k_ref)) + (return) + ) + + (func $yield42 (result i32) + (block $h (result i32) + (try_table (result i32) (catch $e0 $h) + (suspend $yield) + (unreachable) + ) + ) + ) + (elem declare func $yield42) +) +(assert_return (invoke "throw_handled_ref") (i32.const 42)) + + +;; Test resume_throw_ref where the continuation does not handle the exception. +(module + (tag $e0) + + (type $f (func)) + (type $k (cont $f)) + + (func $no_handler + (unreachable) ;; We only throw into this function + ) + (elem declare func $no_handler) + + (func (export "throw_unhandled_ref") + (block $h (result exnref) + (try_table (catch_ref $e0 $h) (throw $e0)) + (unreachable) + ) + (resume_throw_ref $k (cont.new $k (ref.func $no_handler))) + ) +) +(assert_exception (invoke "throw_unhandled_ref")) + +;; Test resume_throw_ref on a consumed continuation. +(module + (tag $e0) + + (type $f (func)) + (type $k (cont $f)) + + (func $f1) + (elem declare func $f1) + + (func (export "throw_consumed_ref") + (local $k_ref (ref $k)) + (local.set $k_ref (cont.new $k (ref.func $f1))) + (resume $k (local.get $k_ref)) ;; consume it + + (block $h (result exnref) + (try_table (result exnref) (catch_ref $e0 $h) + (throw $e0) + ) + ) + (local.get $k_ref) + + (resume_throw_ref $k) ;; should trap + ) +) +(assert_trap (invoke "throw_consumed_ref") "continuation already consumed") + +;; Test resume_throw_ref on a null continuation reference. +(module + (tag $e0) + (type $f (func)) + (type $k (cont $f)) + (func (export "throw_null_ref") + (block $h (result exnref) + (try_table (catch_ref $e0 $h) + (throw $e0)) + (unreachable) + ) + (resume_throw_ref $k (ref.null $k)) + ) +) +(assert_trap (invoke "throw_null_ref") "null continuation reference") + +;; ---- Validation ---- + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $exn (param i32)) + (func + (i64.const 0) + (resume_throw $ct $exn (ref.null $ct)) ;; null continuation + (unreachable))) + "type mismatch") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $exn) + (func + (ref.null $ct) + (i32.const 0) + (resume_throw $ct $exn) ;; exception tag does not take paramter + (unreachable))) + "type mismatch") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $exn (param i32)) + (func + (resume_throw $ct $exn (ref.null $ct)) ;; missing exception payload + (unreachable))) + "type mismatch") + + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (tag $exn (param externref)) + (func + (i64.const 0) + (resume_throw_ref $ct (ref.null $ct)) ;; expecting an exception ref + (unreachable))) + "type mismatch") + +(assert_invalid + (module + (type $ft (func)) + (type $ct (cont $ft)) + (func + (resume_throw_ref $ct (ref.null $ct)) ;; expecting an exception ref + (unreachable))) + "type mismatch") diff --git a/test/core/stack-switching/validation.wast b/test/core/stack-switching/validation.wast new file mode 100644 index 000000000..116b3d17f --- /dev/null +++ b/test/core/stack-switching/validation.wast @@ -0,0 +1,903 @@ +;; This file tests validation only, without GC types and subtyping. + + +;;;; +;;;; WasmFX types +;;;; + +(module + (type $ft1 (func)) + (type $ct1 (cont $ft1)) + + (type $ft2 (func (param i32) (result i32))) + (type $ct2 (cont $ft2)) + + (func $test + (param $p1 (ref cont)) + (param $p2 (ref nocont)) + (param $p3 (ref $ct1)) + + (local $x1 (ref cont)) + (local $x2 (ref nocont)) + (local $x3 (ref $ct1)) + (local $x4 (ref $ct2)) + (local $x5 (ref null $ct1)) + + ;; nocont <: cont + (local.set $x1 (local.get $p2)) + + ;; nocont <: $ct1 + (local.set $x3 (local.get $p2)) + + ;; $ct1 <: $cont + (local.set $x3 (local.get $p3)) + + ;; (ref $ct1) <: (ref null $cont) + (local.set $x5 (local.get $p3)) + ) +) + +(assert_invalid + (module + (type $ft1 (func)) + (type $ct1 (cont $ft1)) + + (type $ft2 (func (param i32) (result i32))) + (type $ct2 (cont $ft2)) + + (func $test + (param $p1 (ref cont)) + (param $p2 (ref nocont)) + (param $p3 (ref $ct1)) + + (local $x1 (ref cont)) + (local $x2 (ref nocont)) + (local $x3 (ref $ct1)) + (local $x4 (ref $ct2)) + (local $x5 (ref null $ct1)) + + ;; cont { + try { + module(bytes, /* valid */ true); + } catch(e) { + throw new Error('failed on custom section error'); + } + }, "A wast module that should have an invalid or malformed custom section."); +} + +const assert_malformed_custom = assert_invalid_custom; + function instance(bytes, imports = registry, valid = true) { if (imports instanceof Result) { if (imports.isError()) @@ -362,12 +374,26 @@ function assert_return(action, ...expected) { // so there's no good way to test that it's a canonical NaN. assert_true(Number.isNaN(actual[i]), `expected NaN, observed ${actual[i]}.`); return; + case "ref.i31": + assert_true(typeof actual[i] === "number" && (actual[i] & 0x7fffffff) === actual[i], `expected Wasm i31, got ${actual[i]}`); + return; + case "ref.any": + case "ref.eq": + case "ref.struct": + case "ref.array": + // For now, JS can't distinguish exported Wasm GC values, + // so we only test for object. + assert_true(typeof actual[i] === "object", `expected Wasm GC object, got ${actual[i]}`); + return; case "ref.func": assert_true(typeof actual[i] === "function", `expected Wasm function, got ${actual[i]}`); return; case "ref.extern": assert_true(actual[i] !== null, `expected Wasm reference, got ${actual[i]}`); return; + case "ref.null": + assert_true(actual[i] === null, `expected Wasm null reference, got ${actual[i]}`); + return; default: assert_equals(actual[i], expected[i]); } diff --git a/test/js-api/exception/basic.tentative.any.js b/test/js-api/exception/basic.tentative.any.js index acf644f90..09f8479aa 100644 --- a/test/js-api/exception/basic.tentative.any.js +++ b/test/js-api/exception/basic.tentative.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker,jsshell +// META: global=window,dedicatedworker,jsshell,shadowrealm // META: script=/wasm/jsapi/wasm-module-builder.js function assert_throws_wasm(fn, message) { @@ -11,14 +11,13 @@ function assert_throws_wasm(fn, message) { } promise_test(async () => { - const kWasmAnyRef = 0x6f; - const kSig_v_r = makeSig([kWasmAnyRef], []); + const kSig_v_r = makeSig([kWasmExternRef], []); const builder = new WasmModuleBuilder(); - const tagIndex = builder.addTag(kSig_v_r); + const tagIndexExternref = builder.addTag(kSig_v_r); builder.addFunction("throw_param", kSig_v_r) .addBody([ kExprLocalGet, 0, - kExprThrow, tagIndex, + kExprThrow, tagIndexExternref, ]) .exportFunc(); const buffer = builder.toBuffer(); @@ -45,11 +44,11 @@ promise_test(async () => { promise_test(async () => { const builder = new WasmModuleBuilder(); - const tagIndex = builder.addTag(kSig_v_a); + const tagIndexAnyref = builder.addTag(kSig_v_a); builder.addFunction("throw_null", kSig_v_v) .addBody([ - kExprRefNull, kWasmAnyFunc, - kExprThrow, tagIndex, + kExprRefNull, kAnyFuncCode, + kExprThrow, tagIndexAnyref, ]) .exportFunc(); const buffer = builder.toBuffer(); @@ -59,11 +58,11 @@ promise_test(async () => { promise_test(async () => { const builder = new WasmModuleBuilder(); - const tagIndex = builder.addTag(kSig_v_i); + const tagIndexI32 = builder.addTag(kSig_v_i); builder.addFunction("throw_int", kSig_v_v) .addBody([ ...wasmI32Const(7), - kExprThrow, tagIndex, + kExprThrow, tagIndexI32, ]) .exportFunc(); const buffer = builder.toBuffer(); @@ -74,15 +73,21 @@ promise_test(async () => { promise_test(async () => { const builder = new WasmModuleBuilder(); const fnIndex = builder.addImport("module", "fn", kSig_v_v); - const tagIndex= builder.addTag(kSig_v_r); + const tagIndexExternref = builder.addTag(kSig_v_r); + builder.addFunction("catch_exception", kSig_r_v) .addBody([ - kExprTry, kWasmStmt, - kExprCallFunction, fnIndex, - kExprCatch, tagIndex, + kExprBlock, kWasmVoid, + kExprBlock, kExternRefCode, + kExprTryTable, kWasmVoid, 1, + kCatchNoRef, tagIndexExternref, 0, + kExprCallFunction, fnIndex, + kExprEnd, + kExprBr, 1, + kExprEnd, kExprReturn, kExprEnd, - kExprRefNull, kWasmAnyRef, + kExprRefNull, kExternRefCode, ]) .exportFunc(); @@ -101,12 +106,17 @@ promise_test(async () => { const fnIndex = builder.addImport("module", "fn", kSig_v_v); builder.addFunction("catch_and_rethrow", kSig_r_v) .addBody([ - kExprTry, kWasmStmt, - kExprCallFunction, fnIndex, - kExprCatchAll, - kExprRethrow, 0x00, + kExprBlock, kWasmVoid, + kExprBlock, kExnRefCode, + kExprTryTable, kWasmVoid, 1, + kCatchAllRef, 0, + kExprCallFunction, fnIndex, + kExprEnd, + kExprBr, 1, + kExprEnd, + kExprThrowRef, kExprEnd, - kExprRefNull, kWasmAnyRef, + kExprRefNull, kExternRefCode, ]) .exportFunc(); @@ -119,3 +129,54 @@ promise_test(async () => { }); assert_throws_exactly(error, () => instance.exports.catch_and_rethrow()); }, "Imported JS function throws, Wasm catches and rethrows"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const fnIndex = builder.addImport("module", "fn", kSig_v_v); + const tagI32 = new WebAssembly.Tag({ parameters: ["i32"] }); + const tagIndexI32 = builder.addImportedTag("module", "tagI32", kSig_v_i); + const exn = new WebAssembly.Exception(tagI32, [42]); + const kSig_ie_v = makeSig([], [kWasmI32, kExnRefCode]); + const sig_ie_v = builder.addType(kSig_ie_v); + + builder.addFunction("all_catch_clauses", kSig_i_v) + .addBody([ + kExprBlock, kWasmVoid, + kExprBlock, kExnRefCode, + kExprBlock, sig_ie_v, + kExprBlock, kWasmVoid, + kExprBlock, kWasmI32, + kExprTryTable, kWasmVoid, 4, + kCatchNoRef, tagIndexI32, 0, + kCatchAllNoRef, 1, + kCatchRef, tagIndexI32, 2, + kCatchAllRef, 3, + kExprCallFunction, fnIndex, + kExprEnd, + kExprBr, 4, + kExprEnd, + kExprReturn, + kExprEnd, + kExprBr, 2, + kExprEnd, + kExprDrop, + kExprDrop, + kExprBr, 1, + kExprEnd, + kExprDrop, + kExprEnd, + kExprI32Const, 0, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const fn = () => { + throw exn; + }; + const {instance} = await WebAssembly.instantiate(buffer, { + module: { fn, tagI32: tagI32 } + }); + const result = instance.exports.all_catch_clauses(); + assert_equals(result, 42); +}, "try-table uses all four kinds of catch clauses, one of which catches an exception"); diff --git a/test/js-api/exception/constructor.tentative.any.js b/test/js-api/exception/constructor.tentative.any.js index 7ad08e188..a46d1816c 100644 --- a/test/js-api/exception/constructor.tentative.any.js +++ b/test/js-api/exception/constructor.tentative.any.js @@ -1,4 +1,4 @@ -// META: global=window,dedicatedworker,jsshell +// META: global=window,dedicatedworker,jsshell,shadowrealm // META: script=/wasm/jsapi/assertions.js test(() => { diff --git a/test/js-api/exception/getArg.tentative.any.js b/test/js-api/exception/getArg.tentative.any.js index 4b72c61f4..6d56ac8c6 100644 --- a/test/js-api/exception/getArg.tentative.any.js +++ b/test/js-api/exception/getArg.tentative.any.js @@ -1,4 +1,4 @@ -// META: global=window,dedicatedworker,jsshell +// META: global=window,dedicatedworker,jsshell,shadowrealm // META: script=/wasm/jsapi/memory/assertions.js test(() => { diff --git a/test/js-api/exception/identity.tentative.any.js b/test/js-api/exception/identity.tentative.any.js index e431197d1..802e7053a 100644 --- a/test/js-api/exception/identity.tentative.any.js +++ b/test/js-api/exception/identity.tentative.any.js @@ -1,4 +1,4 @@ -// META: global=window,dedicatedworker,jsshell +// META: global=window,dedicatedworker,jsshell,shadowrealm // META: script=/wasm/jsapi/assertions.js // META: script=/wasm/jsapi/wasm-module-builder.js @@ -22,6 +22,9 @@ test(() => { let wasmTagExnSamePayload = null; let wasmTagExnDiffPayload = null; + const kSig_ie_v = makeSig([], [kWasmI32, kExnRefCode]); + const sig_ie_v = builder.addType(kSig_ie_v); + const imports = { module: { throwJSTagExn: function() { throw jsTagExn; }, @@ -31,55 +34,73 @@ test(() => { }; // Call a JS function that throws an exception using a JS-defined tag, catches - // it with a 'catch' instruction, and rethrows it. + // it with a 'catch_ref' instruction, and rethrows it. builder - .addFunction("catch_js_tag_rethrow", kSig_v_v) + .addFunction("catch_ref_js_tag_throw_ref", kSig_v_v) .addBody([ - kExprTry, kWasmStmt, - kExprCallFunction, throwJSTagExnIndex, - kExprCatch, jsTagIndex, - kExprDrop, - kExprRethrow, 0x00, + kExprBlock, kWasmVoid, + kExprBlock, sig_ie_v, + kExprTryTable, kWasmVoid, 1, + kCatchRef, jsTagIndex, 0, + kExprCallFunction, throwJSTagExnIndex, + kExprEnd, + kExprBr, 1, + kExprEnd, + kExprThrowRef, kExprEnd ]) .exportFunc(); // Call a JS function that throws an exception using a Wasm-defined tag, - // catches it with a 'catch' instruction, and rethrows it. + // catches it with a 'catch_ref' instruction, and rethrows it. builder - .addFunction("catch_wasm_tag_rethrow", kSig_v_v) + .addFunction("catch_ref_wasm_tag_throw_ref", kSig_v_v) .addBody([ - kExprTry, kWasmStmt, - kExprCallFunction, throwWasmTagExnIndex, - kExprCatch, wasmTagIndex, - kExprDrop, - kExprRethrow, 0x00, + kExprBlock, kWasmVoid, + kExprBlock, sig_ie_v, + kExprTryTable, kWasmVoid, 1, + kCatchRef, wasmTagIndex, 0, + kExprCallFunction, throwWasmTagExnIndex, + kExprEnd, + kExprBr, 1, + kExprEnd, + kExprThrowRef, kExprEnd ]) .exportFunc(); // Call a JS function that throws an exception using a JS-defined tag, catches - // it with a 'catch_all' instruction, and rethrows it. + // it with a 'catch_all_ref' instruction, and rethrows it. builder - .addFunction("catch_all_js_tag_rethrow", kSig_v_v) + .addFunction("catch_all_ref_js_tag_throw_ref", kSig_v_v) .addBody([ - kExprTry, kWasmStmt, - kExprCallFunction, throwJSTagExnIndex, - kExprCatchAll, - kExprRethrow, 0x00, + kExprBlock, kWasmVoid, + kExprBlock, kExnRefCode, + kExprTryTable, kWasmVoid, 1, + kCatchAllRef, 0, + kExprCallFunction, throwJSTagExnIndex, + kExprEnd, + kExprBr, 1, + kExprEnd, + kExprThrowRef, kExprEnd ]) .exportFunc(); // Call a JS function that throws an exception using a Wasm-defined tag, - // catches it with a 'catch_all' instruction, and rethrows it. + // catches it with a 'catch_all_ref' instruction, and rethrows it. builder - .addFunction("catch_all_wasm_tag_rethrow", kSig_v_v) + .addFunction("catch_all_ref_wasm_tag_throw_ref", kSig_v_v) .addBody([ - kExprTry, kWasmStmt, - kExprCallFunction, throwWasmTagExnIndex, - kExprCatchAll, - kExprRethrow, 0x00, + kExprBlock, kWasmVoid, + kExprBlock, kExnRefCode, + kExprTryTable, kWasmVoid, 1, + kCatchAllRef, 0, + kExprCallFunction, throwWasmTagExnIndex, + kExprEnd, + kExprBr, 1, + kExprEnd, + kExprThrowRef, kExprEnd ]) .exportFunc(); @@ -89,12 +110,17 @@ test(() => { builder .addFunction("catch_js_tag_return_payload", kSig_i_v) .addBody([ - kExprTry, kWasmI32, - kExprCallFunction, throwJSTagExnIndex, - kExprI32Const, 0x00, - kExprCatch, jsTagIndex, + kExprBlock, kWasmVoid, + kExprBlock, kWasmI32, + kExprTryTable, kWasmVoid, 1, + kCatchNoRef, jsTagIndex, 0, + kExprCallFunction, throwJSTagExnIndex, + kExprEnd, + kExprBr, 1, + kExprEnd, kExprReturn, - kExprEnd + kExprEnd, + kExprI32Const, 0 ]) .exportFunc(); @@ -103,9 +129,14 @@ test(() => { builder .addFunction("catch_js_tag_throw_payload", kSig_v_v) .addBody([ - kExprTry, kWasmStmt, - kExprCallFunction, throwJSTagExnIndex, - kExprCatch, jsTagIndex, + kExprBlock, kWasmVoid, + kExprBlock, kWasmI32, + kExprTryTable, kWasmVoid, 1, + kCatchNoRef, jsTagIndex, 0, + kExprCallFunction, throwJSTagExnIndex, + kExprEnd, + kExprBr, 1, + kExprEnd, kExprThrow, jsTagIndex, kExprEnd ]) @@ -117,7 +148,7 @@ test(() => { // The exception object's identity should be preserved across 'rethrow's in // Wasm code. Do tests with a tag defined in JS. try { - result.instance.exports.catch_js_tag_rethrow(); + result.instance.exports.catch_ref_js_tag_throw_ref(); } catch (e) { assert_equals(e, jsTagExn); // Even if they have the same payload, they are different objects, so they @@ -126,7 +157,7 @@ test(() => { assert_not_equals(e, jsTagExnDiffPayload); } try { - result.instance.exports.catch_all_js_tag_rethrow(); + result.instance.exports.catch_all_ref_js_tag_throw_ref(); } catch (e) { assert_equals(e, jsTagExn); assert_not_equals(e, jsTagExnSamePayload); @@ -139,14 +170,14 @@ test(() => { wasmTagExnSamePayload = new WebAssembly.Exception(wasmTag, [42]); wasmTagExnDiffPayload = new WebAssembly.Exception(wasmTag, [53]); try { - result.instance.exports.catch_wasm_tag_rethrow(); + result.instance.exports.catch_ref_wasm_tag_throw_ref(); } catch (e) { assert_equals(e, wasmTagExn); assert_not_equals(e, wasmTagExnSamePayload); assert_not_equals(e, wasmTagExnDiffPayload); } try { - result.instance.exports.catch_all_wasm_tag_rethrow(); + result.instance.exports.catch_all_ref_wasm_tag_throw_ref(); } catch (e) { assert_equals(e, wasmTagExn); assert_not_equals(e, wasmTagExnSamePayload); diff --git a/test/js-api/exception/is.tentative.any.js b/test/js-api/exception/is.tentative.any.js index e28a88a3c..840d00bf0 100644 --- a/test/js-api/exception/is.tentative.any.js +++ b/test/js-api/exception/is.tentative.any.js @@ -1,4 +1,4 @@ -// META: global=window,dedicatedworker,jsshell +// META: global=window,dedicatedworker,jsshell,shadowrealm // META: script=/wasm/jsapi/memory/assertions.js test(() => { diff --git a/test/js-api/exception/jsTag.tentative.any.js b/test/js-api/exception/jsTag.tentative.any.js new file mode 100644 index 000000000..c5db55370 --- /dev/null +++ b/test/js-api/exception/jsTag.tentative.any.js @@ -0,0 +1,121 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/wasm-module-builder.js + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Exception(WebAssembly.JSTag, [{}])) +}, "Creating a WebAssembly.Exception with JSTag explicitly is not allowed"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const jsTag = builder.addImportedTag("module", "JSTag", kSig_v_r); + + const throwRefFn = builder.addImport("module", "throw_ref", kSig_r_r); + const sig_r_v = builder.addType(kSig_r_v); + const kSig_re_v = makeSig([], [kExternRefCode, kExnRefCode]); + const sig_re_v = builder.addType(kSig_re_v); + + // Calls throw_ref, catches an exception with 'try_table - catch JSTag', and + // returns it + builder.addFunction("catch_js_tag_and_return", kSig_r_r) + .addBody([ + kExprBlock, sig_r_v, + kExprTryTable, sig_r_v, 1, + kCatchNoRef, jsTag, 0, + kExprLocalGet, 0, + kExprCallFunction, throwRefFn, + kExprEnd, + kExprEnd, + ]) + .exportFunc(); + + // Calls throw_ref, catches an exception with 'try_table - catch_ref JSTag', + // and returns it + builder.addFunction("catch_ref_js_tag_and_return", kSig_r_r) + .addBody([ + kExprBlock, sig_re_v, + kExprTryTable, sig_r_v, 1, + kCatchRef, jsTag, 0, + kExprLocalGet, 0, + kExprCallFunction, throwRefFn, + kExprEnd, + kExprReturn, + kExprEnd, + kExprDrop, + ]) + .exportFunc(); + + // Calls throw_ref, catches an exception with 'try_table - catch_ref JSTag', + // and rethrows it (with throw_ref) + builder.addFunction("catch_ref_js_tag_and_throw_ref", kSig_r_r) + .addBody([ + kExprBlock, sig_re_v, + kExprTryTable, sig_r_v, 1, + kCatchRef, jsTag, 0, + kExprLocalGet, 0, + kExprCallFunction, throwRefFn, + kExprEnd, + kExprReturn, + kExprEnd, + kExprThrowRef, + ]) + .exportFunc(); + + function throw_ref(x) { + throw x; + } + const buffer = builder.toBuffer(); + const {instance} = await WebAssembly.instantiate(buffer, { + module: { throw_ref, JSTag: WebAssembly.JSTag } + }); + + const obj = {}; + const wasmTag = new WebAssembly.Tag({parameters:['externref']}); + const exn = new WebAssembly.Exception(wasmTag, [obj]); + + // Test catch w/ return: + // This throws obj as a JS exception so it should be caught by the program and + // be returned as the original obj. + assert_equals(obj, instance.exports.catch_js_tag_and_return(obj)); + // This is a WebAssembly.Exception, so the exception should just pass through + // the program without being caught. + assert_throws_exactly(exn, () => instance.exports.catch_js_tag_and_return(exn)); + + // Test catch_ref w/ return: + // This throws obj as a JS exception so it should be caught by the program and + // be returned as the original obj. + assert_equals(obj, instance.exports.catch_ref_js_tag_and_return(obj)); + // This is a WebAssembly.Exception, so the exception should just pass through + // the program without being caught. + assert_throws_exactly(exn, () => instance.exports.catch_ref_js_tag_and_return(exn)); + + // Test catch_ref w/ throw_ref: + // This throws obj as a JS exception so it should be caught by the program and + // be rethrown. + assert_throws_exactly(obj, () => instance.exports.catch_ref_js_tag_and_throw_ref(obj)); + // This is a WebAssembly.Exception, so the exception should just pass through + // the program without being caught. + assert_throws_exactly(exn, () => instance.exports.catch_ref_js_tag_and_throw_ref(exn)); +}, "JS tag catching tests"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const jsTag = builder.addImportedTag("module", "JSTag", kSig_v_r); + + // Throw a JS object with WebAssembly.JSTag and check that we can catch it + // as-is from JavaScript. + builder.addFunction("throw_js_tag", kSig_v_r) + .addBody([ + kExprLocalGet, 0, + kExprThrow, jsTag, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + const {instance} = await WebAssembly.instantiate(buffer, { + module: { JSTag: WebAssembly.JSTag } + }); + + const obj = {}; + assert_throws_exactly(obj, () => instance.exports.throw_js_tag(obj)); +}, "JS tag throwing test"); diff --git a/test/js-api/exception/toString.tentative.any.js b/test/js-api/exception/toString.tentative.any.js index 00e801a6f..6885cf0de 100644 --- a/test/js-api/exception/toString.tentative.any.js +++ b/test/js-api/exception/toString.tentative.any.js @@ -1,4 +1,4 @@ -// META: global=window,dedicatedworker,jsshell +// META: global=window,dedicatedworker,jsshell,shadowrealm test(() => { const argument = { parameters: [] }; diff --git a/test/js-api/gc/default-value.tentative.any.js b/test/js-api/gc/default-value.tentative.any.js new file mode 100644 index 000000000..d828b4d87 --- /dev/null +++ b/test/js-api/gc/default-value.tentative.any.js @@ -0,0 +1,43 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +let exports = {}; +setup(() => { + const builder = new WasmModuleBuilder(); + + builder.addTable(wasmRefType(kWasmAnyRef), 10, 20, [...wasmI32Const(42), ...GCInstr(kExprRefI31)]) + .exportAs("tableAnyNonNullable"); + builder.addTable(wasmRefNullType(kWasmAnyRef), 10, 20) + .exportAs("tableAnyNullable"); + + const buffer = builder.toBuffer(); + const module = new WebAssembly.Module(buffer); + const instance = new WebAssembly.Instance(module, {}); + exports = instance.exports; +}); + +test(() => { + exports.tableAnyNullable.grow(5); + for (let i = 0; i < 5; i++) + assert_equals(exports.tableAnyNullable.get(10 + i), null); +}, "grow (nullable anyref)"); + +test(() => { + assert_throws_js(TypeError, () => { exports.tableAnyNonNullable.grow(5); }); + exports.tableAnyNonNullable.grow(5, "foo"); + for (let i = 0; i < 5; i++) + assert_equals(exports.tableAnyNonNullable.get(10 + i), "foo"); +}, "grow (non-nullable anyref)"); + +test(() => { + for (let i = 0; i < exports.tableAnyNullable.length; i++) { + exports.tableAnyNullable.set(i); + assert_equals(exports.tableAnyNullable.get(i), null); + } +}, "set (nullable anyref)"); + +test(() => { + for (let i = 0; i < exports.tableAnyNonNullable.length; i++) { + assert_throws_js(TypeError, () => { exports.tableAnyNonNullable.set(i); }); + } +}, "set (non-nullable anyref)"); diff --git a/test/js-api/gc/i31.tentative.any.js b/test/js-api/gc/i31.tentative.any.js index 17fd82440..d8cfe424e 100644 --- a/test/js-api/gc/i31.tentative.any.js +++ b/test/js-api/gc/i31.tentative.any.js @@ -11,7 +11,7 @@ setup(() => { builder .addFunction("makeI31", makeSig_r_x(i31Ref, kWasmI32)) .addBody([kExprLocalGet, 0, - ...GCInstr(kExprI31New)]) + ...GCInstr(kExprRefI31)]) .exportFunc(); builder @@ -33,7 +33,7 @@ setup(() => { .exportFunc(); builder - .addGlobal(i31NullableRef, true, [...wasmI32Const(0), ...GCInstr(kExprI31New)]) + .addGlobal(i31NullableRef, true, [...wasmI32Const(0), ...GCInstr(kExprRefI31)]) builder .addExportOfKind("i31Global", kExternalGlobal, 0); diff --git a/test/js-api/memory/assertions.js b/test/js-api/memory/assertions.js index b539513ad..4407a0403 100644 --- a/test/js-api/memory/assertions.js +++ b/test/js-api/memory/assertions.js @@ -27,7 +27,7 @@ function assert_ArrayBuffer(actual, { size=0, shared=false, detached=false }, me assert_equals(Object.isExtensible(actual), !shared, "buffer extensibility"); } -function assert_Memory(memory, { size=0, shared=false }) { +function assert_Memory(memory, { size=0, shared=false, address="i32" }) { assert_equals(Object.getPrototypeOf(memory), WebAssembly.Memory.prototype, "prototype"); assert_true(Object.isExtensible(memory), "extensible"); @@ -35,4 +35,9 @@ function assert_Memory(memory, { size=0, shared=false }) { // https://github.com/WebAssembly/spec/issues/840 assert_equals(memory.buffer, memory.buffer, "buffer should be idempotent"); assert_ArrayBuffer(memory.buffer, { size, shared }); + + // this depends on js-types proposal implementation + if (typeof memory.type == "function") { + assert_equals(memory.type().address, address, "memory address type"); + } } diff --git a/test/js-api/memory/constructor.any.js b/test/js-api/memory/constructor.any.js index 0a0be11e3..05953f276 100644 --- a/test/js-api/memory/constructor.any.js +++ b/test/js-api/memory/constructor.any.js @@ -62,10 +62,29 @@ for (const value of outOfRangeValues) { }, `Out-of-range maximum value in descriptor: ${format_value(value)}`); } +const outOfRangeValuesI64 = [ + -1n, + 0x1_0000_0000_0000_0000n, +]; + +for (const value of outOfRangeValuesI64) { + test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Memory({ "address": "i64", "initial": value })); + }, `Out-of-range initial i64 value in descriptor: ${format_value(value)}`); + + test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Memory({ "address": "i64", "initial": 0n, "maximum": value })); + }, `Out-of-range maximum i64 value in descriptor: ${format_value(value)}`); +} + test(() => { assert_throws_js(RangeError, () => new WebAssembly.Memory({ "initial": 10, "maximum": 9 })); }, "Initial value exceeds maximum"); +test(() => { + assert_throws_js(RangeError, () => new WebAssembly.Memory({ "address": "i64", "initial": 10n, "maximum": 9n })); +}, "Initial value exceeds maximum (i64)"); + test(() => { const proxy = new Proxy({}, { has(o, x) { @@ -79,6 +98,8 @@ test(() => { case "initial": case "maximum": return 0; + case "address": + return "i32"; default: return undefined; } @@ -110,9 +131,21 @@ test(() => { }, }; }, + + get address() { + order.push("address"); + return { + toString() { + order.push("address toString"); + return "i32"; + }, + }; + }, }); assert_array_equals(order, [ + "address", + "address toString", "initial", "initial valueOf", "maximum", @@ -132,8 +165,66 @@ test(() => { assert_Memory(memory, { "size": 4 }); }, "Non-zero initial"); +test(() => { + const argument = { "address": "i64", "initial": 0n }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 0, "address": "i64" }); +}, "Zero initial (i64)"); + +test(() => { + const argument = { "address": "i64", "initial": 4n }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 4, "address": "i64" }); +}, "Non-zero initial (i64)"); + test(() => { const argument = { "initial": 0 }; const memory = new WebAssembly.Memory(argument, {}); assert_Memory(memory, { "size": 0 }); }, "Stray argument"); + +test(() => { + const argument = { "initial": 1 }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 1, "address": "i32" }); +}, "Memory with address parameter omitted"); + +test(() => { + const argument = { "initial": 1, "address": "i32" }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 1, "address": "i32" }); +}, "Memory with i32 address constructor"); + +test(() => { + const argument = { "initial": 1n, "address": "i64" }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 1, "address": "i64" }); +}, "Memory with i64 address constructor"); + +test(() => { + const argument = { "initial": "3" }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 3 }); +}, "Memory with string value for initial"); + +test(() => { + const argument = { "address": "i64", "initial": "3" }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 3, "address": "i64" }); +}, "Memory with string value for initial (i64)"); + +test(() => { + const argument = { "initial": true }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 1 }); +}, "Memory with boolean value for initial"); + +test(() => { + const argument = { "address": "i64", "initial": true }; + const memory = new WebAssembly.Memory(argument); + assert_Memory(memory, { "size": 1, "address": "i64" }); +}, "Memory with boolean value for initial (i64)"); + +test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": 1, "address": "none" })); +}, "Unknown memory address"); diff --git a/test/js-api/memory/grow.any.js b/test/js-api/memory/grow.any.js index c51112949..cb1a9a6f3 100644 --- a/test/js-api/memory/grow.any.js +++ b/test/js-api/memory/grow.any.js @@ -47,6 +47,21 @@ test(() => { assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing"); }, "Zero initial"); +test(() => { + const argument = { "address": "i64", "initial": 0n }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing"); + + const result = memory.grow(2n); + assert_equals(result, 0n); + + const newMemory = memory.buffer; + assert_not_equals(oldMemory, newMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing"); + assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing"); +}, "Zero initial (i64)"); + test(() => { const argument = { "initial": { valueOf() { return 0 } } }; const memory = new WebAssembly.Memory(argument); @@ -77,6 +92,21 @@ test(() => { assert_ArrayBuffer(newMemory, { "size": 5 }, "New buffer after growing"); }, "Non-zero initial"); +test(() => { + const argument = { "address": "i64", "initial": 3n }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 3 }, "Buffer before growing"); + + const result = memory.grow(2n); + assert_equals(result, 3n); + + const newMemory = memory.buffer; + assert_not_equals(oldMemory, newMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing"); + assert_ArrayBuffer(newMemory, { "size": 5 }, "New buffer after growing"); +}, "Non-zero initial (i64)"); + test(() => { const argument = { "initial": 0, "maximum": 2 }; const memory = new WebAssembly.Memory(argument); @@ -92,6 +122,21 @@ test(() => { assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing"); }, "Zero initial with respected maximum"); +test(() => { + const argument = { "address": "i64", "initial": 0n, "maximum": 2n }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing"); + + const result = memory.grow(2n); + assert_equals(result, 0n); + + const newMemory = memory.buffer; + assert_not_equals(oldMemory, newMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing"); + assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing"); +}, "Zero initial with respected maximum (i64)"); + test(() => { const argument = { "initial": 0, "maximum": 2 }; const memory = new WebAssembly.Memory(argument); @@ -116,6 +161,30 @@ test(() => { assert_ArrayBuffer(newestMemory, { "size": 2 }, "Newest buffer after growing twice"); }, "Zero initial with respected maximum grown twice"); +test(() => { + const argument = { "address": "i64", "initial": 0n, "maximum": 2n }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing"); + + const result = memory.grow(1n); + assert_equals(result, 0n); + + const newMemory = memory.buffer; + assert_not_equals(oldMemory, newMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing once"); + assert_ArrayBuffer(newMemory, { "size": 1 }, "New buffer after growing once"); + + const result2 = memory.grow(1n); + assert_equals(result2, 1n); + + const newestMemory = memory.buffer; + assert_not_equals(newMemory, newestMemory); + assert_ArrayBuffer(oldMemory, { "detached": true }, "New buffer after growing twice"); + assert_ArrayBuffer(newMemory, { "detached": true }, "New buffer after growing twice"); + assert_ArrayBuffer(newestMemory, { "size": 2 }, "Newest buffer after growing twice"); +}, "Zero initial with respected maximum grown twice (i64)"); + test(() => { const argument = { "initial": 1, "maximum": 2 }; const memory = new WebAssembly.Memory(argument); @@ -127,6 +196,17 @@ test(() => { assert_ArrayBuffer(memory.buffer, { "size": 1 }, "Buffer before trying to grow"); }, "Zero initial growing too much"); +test(() => { + const argument = { "address": "i64", "initial": 1n, "maximum": 2n }; + const memory = new WebAssembly.Memory(argument); + const oldMemory = memory.buffer; + assert_ArrayBuffer(oldMemory, { "size": 1 }, "Buffer before growing"); + + assert_throws_js(RangeError, () => memory.grow(2n)); + assert_equals(memory.buffer, oldMemory); + assert_ArrayBuffer(memory.buffer, { "size": 1 }, "Buffer before trying to grow"); +}, "Zero initial growing too much (i64)"); + const outOfRangeValues = [ undefined, NaN, @@ -147,6 +227,20 @@ for (const value of outOfRangeValues) { }, `Out-of-range argument: ${format_value(value)}`); } +const outOfRangeValuesI64 = [ + -1n, + 0x10000000000000000n, + "0x10000000000000000", +]; + +for (const value of outOfRangeValuesI64) { + test(() => { + const argument = { "address": "i64", "initial": 0n }; + const memory = new WebAssembly.Memory(argument); + assert_throws_js(TypeError, () => memory.grow(value)); + }, `Out-of-range i64 argument: ${format_value(value)}`); +} + test(() => { const argument = { "initial": 0 }; const memory = new WebAssembly.Memory(argument); diff --git a/test/js-api/table/assertions.js b/test/js-api/table/assertions.js index 19cc5c3b9..cd403b480 100644 --- a/test/js-api/table/assertions.js +++ b/test/js-api/table/assertions.js @@ -1,24 +1,28 @@ -function assert_equal_to_array(table, expected, message) { - assert_equals(table.length, expected.length, `${message}: length`); +function assert_equal_to_array(table, expected, message, address = "i32") { + function addr(i) { + return address === "i64" ? BigInt(i) : i; + } + + assert_equals(table.length, addr(expected.length), `${message}: length`); // The argument check in get() happens before the range check, and negative numbers // are illegal, hence will throw TypeError per spec. - assert_throws_js(TypeError, () => table.get(-1), `${message}: table.get(-1)`); + assert_throws_js(TypeError, () => table.get(addr(-1)), `${message}: table.get(-1)`); for (let i = 0; i < expected.length; ++i) { - assert_equals(table.get(i), expected[i], `${message}: table.get(${i} of ${expected.length})`); + assert_equals(table.get(addr(i)), expected[i], `${message}: table.get(${i} of ${expected.length})`); } - assert_throws_js(RangeError, () => table.get(expected.length), + assert_throws_js(RangeError, () => table.get(addr(expected.length)), `${message}: table.get(${expected.length} of ${expected.length})`); - assert_throws_js(RangeError, () => table.get(expected.length + 1), + assert_throws_js(RangeError, () => table.get(addr(expected.length + 1)), `${message}: table.get(${expected.length + 1} of ${expected.length})`); } -function assert_Table(actual, expected) { +function assert_Table(actual, expected, address = "i32") { assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype, "prototype"); assert_true(Object.isExtensible(actual), "extensible"); assert_equals(actual.length, expected.length, "length"); - for (let i = 0; i < expected.length; ++i) { + for (let i = address === "i64" ? 0n : 0; i < expected.length; ++i) { assert_equals(actual.get(i), null, `actual.get(${i})`); } } diff --git a/test/js-api/table/constructor.any.js b/test/js-api/table/constructor.any.js index 6d38d04e4..ebcd98b52 100644 --- a/test/js-api/table/constructor.any.js +++ b/test/js-api/table/constructor.any.js @@ -71,10 +71,29 @@ for (const value of outOfRangeValues) { }, `Out-of-range maximum value in descriptor: ${format_value(value)}`); } +const outOfRangeValuesI64 = [ + -1n, + 0x1_0000_0000_0000_0000n, +]; + +for (const value of outOfRangeValuesI64) { + test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Table({ "element": "anyfunc", "address": "i64", "initial": value })); + }, `Out-of-range initial i64 value in descriptor: ${format_value(value)}`); + + test(() => { + assert_throws_js(TypeError, () => new WebAssembly.Table({ "element": "anyfunc", "address": "i64", "initial": 0n, "maximum": value })); + }, `Out-of-range maximum i64 value in descriptor: ${format_value(value)}`); +} + test(() => { assert_throws_js(RangeError, () => new WebAssembly.Table({ "element": "anyfunc", "initial": 10, "maximum": 9 })); }, "Initial value exceeds maximum"); +test(() => { + assert_throws_js(RangeError, () => new WebAssembly.Table({ "element": "anyfunc", "address": "i64", "initial": 10n, "maximum": 9n })); +}, "Initial value exceeds maximum (i64)"); + test(() => { const argument = { "element": "anyfunc", "initial": 0 }; const table = new WebAssembly.Table(argument); @@ -87,6 +106,18 @@ test(() => { assert_Table(table, { "length": 5 }); }, "Basic (non-zero)"); +test(() => { + const argument = { "element": "anyfunc", "address": "i64", "initial": 0n }; + const table = new WebAssembly.Table(argument); + assert_Table(table, { "length": 0n }, "i64"); +}, "Basic (zero, i64)"); + +test(() => { + const argument = { "element": "anyfunc", "address": "i64", "initial": 5n }; + const table = new WebAssembly.Table(argument); + assert_Table(table, { "length": 5n }, "i64"); +}, "Basic (non-zero, i64)"); + test(() => { const argument = { "element": "anyfunc", "initial": 0 }; const table = new WebAssembly.Table(argument, null, {}); @@ -157,11 +188,23 @@ test(() => { }, }; }, + + get address() { + order.push("address"); + return { + toString() { + order.push("address toString"); + return "i32"; + }, + }; + }, }); assert_array_equals(order, [ "element", "element toString", + "address", + "address toString", "initial", "initial valueOf", "maximum", @@ -206,3 +249,96 @@ test(() => { assert_throws_js(TypeError, () => new WebAssembly.Table(argument, "cannot be used as a wasm function")); assert_throws_js(TypeError, () => new WebAssembly.Table(argument, 37)); }, "initialize anyfunc table with a bad default value"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 3, "address": "i32" }; + const table = new WebAssembly.Table(argument); + // Once this is merged with the type reflection proposal we should check the + // address type of `table`. + assert_equals(table.length, 3); +}, "Table with i32 address constructor"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 3n, "address": "i64" }; + const table = new WebAssembly.Table(argument); + // Once this is merged with the type reflection proposal we should check the + // address type of `table`. + assert_equals(table.length, 3n); +}, "Table with i64 address constructor"); + +test(() => { + const argument = { "element": "anyfunc", "initial": "3", "address": "i32" }; + const table = new WebAssembly.Table(argument); + assert_equals(table.length, 3); +}, "Table with string value for initial"); + +test(() => { + const argument = { "element": "anyfunc", "initial": "3", "address": "i64" }; + const table = new WebAssembly.Table(argument); + assert_equals(table.length, 3n); +}, "Table with string value for initial (i64)"); + +test(() => { + const argument = { "element": "anyfunc", "initial": true, "address": "i32" }; + const table = new WebAssembly.Table(argument); + assert_equals(table.length, 1); +}, "Table with boolean value for initial"); + +test(() => { + const argument = { "element": "anyfunc", "initial": true, "address": "i64" }; + const table = new WebAssembly.Table(argument); + assert_equals(table.length, 1n); +}, "Table with boolean value for initial (i64)"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 0, "maximum": "3", "address": "i32" }; + const table = new WebAssembly.Table(argument); + table.grow(3); + assert_equals(table.length, 3); +}, "Table with string value for maximum"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 0n, "maximum": "3", "address": "i64" }; + const table = new WebAssembly.Table(argument); + table.grow(3n); + assert_equals(table.length, 3n); +}, "Table with string value for maximum (i64)"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 0, "maximum": true, "address": "i32" }; + const table = new WebAssembly.Table(argument); + table.grow(1); + assert_equals(table.length, 1); +}, "Table with boolean value for maximum"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 0n, "maximum": true, "address": "i64" }; + const table = new WebAssembly.Table(argument); + table.grow(1n); + assert_equals(table.length, 1n); +}, "Table with boolean value for maximum (i64)"); + +test(() => { + const argument = { "element": "anyfunc", "initial": 3, "address": "unknown" }; + assert_throws_js(TypeError, () => new WebAssembly.Table(argument)); +}, "Unknown table address"); + +test(() => { + const argument = { "element": "i32", "initial": 3n }; + assert_throws_js(TypeError, () => new WebAssembly.Table(argument)); +}, "initialize table with a wrong initial type"); + +test(() => { + const argument = { "element": "i32", "initial": 3, "maximum": 10n }; + assert_throws_js(TypeError, () => new WebAssembly.Table(argument)); +}, "initialize table with a wrong maximum type"); + +test(() => { + const argument = { "element": "i32", "initial": 3 }; + assert_throws_js(TypeError, () => new WebAssembly.Table(argument)); +}, "initialize table with a wrong initial type (i64)"); + +test(() => { + const argument = { "element": "i32", "initial": 3n, "maximum": 10 }; + assert_throws_js(TypeError, () => new WebAssembly.Table(argument)); +}, "initialize table with a wrong maximum type (i64)"); diff --git a/test/js-api/table/get-set.any.js b/test/js-api/table/get-set.any.js index 9301057a5..c656cd29c 100644 --- a/test/js-api/table/get-set.any.js +++ b/test/js-api/table/get-set.any.js @@ -100,6 +100,23 @@ test(() => { assert_equal_to_array(table, [null, null, fn2, null, fn]); }, "Basic"); +test(() => { + const argument = { "element": "anyfunc", "address": "i64", "initial": 5n }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, [null, null, null, null, null], undefined, "i64"); + + const {fn, fn2} = functions; + + assert_equals(table.set(0n, fn), undefined, "set() returns undefined."); + table.set(2n, fn2); + table.set(4n, fn); + + assert_equal_to_array(table, [fn, null, fn2, null, fn], undefined, "i64"); + + table.set(0n, null); + assert_equal_to_array(table, [null, null, fn2, null, fn], undefined, "i64"); +}, "Basic (i64)"); + test(() => { const argument = { "element": "anyfunc", "initial": 5 }; const table = new WebAssembly.Table(argument); @@ -118,6 +135,24 @@ test(() => { assert_equal_to_array(table, [fn, null, fn2, null, fn, null, null, null, null]); }, "Growing"); +test(() => { + const argument = { "element": "anyfunc", "address": "i64", "initial": 5n }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, [null, null, null, null, null], undefined, "i64"); + + const {fn, fn2} = functions; + + table.set(0n, fn); + table.set(2n, fn2); + table.set(4n, fn); + + assert_equal_to_array(table, [fn, null, fn2, null, fn], undefined, "i64"); + + table.grow(4n); + + assert_equal_to_array(table, [fn, null, fn2, null, fn, null, null, null, null], undefined, "i64"); +}, "Growing (i64)"); + test(() => { const argument = { "element": "anyfunc", "initial": 5 }; const table = new WebAssembly.Table(argument); @@ -132,6 +167,20 @@ test(() => { assert_equal_to_array(table, [null, null, null, null, null]); }, "Setting out-of-bounds"); +test(() => { + const argument = { "element": "anyfunc", "address": "i64", "initial": 5n }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, [null, null, null, null, null], undefined, "i64"); + + const {fn} = functions; + + // -1 is the wrong type hence the type check on entry gets this + // before the range check does. + assert_throws_js(TypeError, () => table.set(-1n, fn)); + assert_throws_js(RangeError, () => table.set(5n, fn)); + assert_equal_to_array(table, [null, null, null, null, null], undefined, "i64"); +}, "Setting out-of-bounds (i64)"); + test(() => { const argument = { "element": "anyfunc", "initial": 1 }; const table = new WebAssembly.Table(argument); @@ -200,6 +249,26 @@ for (const value of outOfRangeValues) { }, `Setting out-of-range argument: ${format_value(value)}`); } +const outOfRangeValuesI64 = [ + -1n, + 0x10000000000000000n, + "0x10000000000000000", +]; + +for (const value of outOfRangeValuesI64) { + test(() => { + const argument = { "element": "anyfunc", "address": "i64", "initial": 1n }; + const table = new WebAssembly.Table(argument); + assert_throws_js(TypeError, () => table.get(value)); + }, `Getting out-of-range argument (i64): ${format_value(value)}`); + + test(() => { + const argument = { "element": "anyfunc", "address": "i64", "initial": 1n }; + const table = new WebAssembly.Table(argument); + assert_throws_js(TypeError, () => table.set(value, null)); + }, `Setting out-of-range argument (i64): ${format_value(value)}`); +} + test(() => { const argument = { "element": "anyfunc", "initial": 1 }; const table = new WebAssembly.Table(argument); diff --git a/test/js-api/table/grow.any.js b/test/js-api/table/grow.any.js index 520d24bf4..55226cbdb 100644 --- a/test/js-api/table/grow.any.js +++ b/test/js-api/table/grow.any.js @@ -45,7 +45,17 @@ test(() => { const result = table.grow(3); assert_equals(result, 5); assert_equal_to_array(table, nulls(8), "after"); -}, "Basic"); +}, "Basic i32"); + +test(() => { + const argument = { "element": "anyfunc", "address": "i64", "initial": 5n }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, nulls(5), "before", "i64"); + + const result = table.grow(3n); + assert_equals(result, 5n); + assert_equal_to_array(table, nulls(8), "after", "i64"); +}, "Basic i64"); test(() => { const argument = { "element": "anyfunc", "initial": 3, "maximum": 5 }; @@ -55,7 +65,17 @@ test(() => { const result = table.grow(2); assert_equals(result, 3); assert_equal_to_array(table, nulls(5), "after"); -}, "Reached maximum"); +}, "Reached maximum (i32)"); + +test(() => { + const argument = { "element": "anyfunc", "address": "i64", "initial": 3n, "maximum": 5n }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, nulls(3), "before", "i64"); + + const result = table.grow(2n); + assert_equals(result, 3n); + assert_equal_to_array(table, nulls(5), "after", "i64"); +}, "Reached maximum (i64)"); test(() => { const argument = { "element": "anyfunc", "initial": 2, "maximum": 5 }; @@ -64,7 +84,16 @@ test(() => { assert_throws_js(RangeError, () => table.grow(4)); assert_equal_to_array(table, nulls(2), "after"); -}, "Exceeded maximum"); +}, "Exceeded maximum (i32)"); + +test(() => { + const argument = { "element": "anyfunc", "address": "i64", "initial": 2n, "maximum": 5n }; + const table = new WebAssembly.Table(argument); + assert_equal_to_array(table, nulls(2), "before", "i64"); + + assert_throws_js(RangeError, () => table.grow(4n)); + assert_equal_to_array(table, nulls(2), "after", "i64"); +}, "Exceeded maximum (i64)"); const outOfRangeValues = [ undefined, @@ -86,6 +115,20 @@ for (const value of outOfRangeValues) { }, `Out-of-range argument: ${format_value(value)}`); } +const outOfRangeValuesI64 = [ + -1n, + 0x10000000000000000n, + "0x10000000000000000", +]; + +for (const value of outOfRangeValuesI64) { + test(() => { + const argument = { "element": "anyfunc", "address": "i64", "initial": 1n }; + const table = new WebAssembly.Table(argument); + assert_throws_js(TypeError, () => table.grow(value)); + }, `Out-of-range i64 argument: ${format_value(value)}`); +} + test(() => { const argument = { "element": "anyfunc", "initial": 5 }; const table = new WebAssembly.Table(argument); diff --git a/test/js-api/tag/constructor.tentative.any.js b/test/js-api/tag/constructor.tentative.any.js index de63e7bf4..54edf8c8f 100644 --- a/test/js-api/tag/constructor.tentative.any.js +++ b/test/js-api/tag/constructor.tentative.any.js @@ -1,4 +1,4 @@ -// META: global=window,dedicatedworker,jsshell +// META: global=window,dedicatedworker,jsshell,shadowrealm // META: script=/wasm/jsapi/assertions.js test(() => { diff --git a/test/js-api/tag/toString.tentative.any.js b/test/js-api/tag/toString.tentative.any.js index ad9a4ba15..76fff0fee 100644 --- a/test/js-api/tag/toString.tentative.any.js +++ b/test/js-api/tag/toString.tentative.any.js @@ -1,4 +1,4 @@ -// META: global=window,dedicatedworker,jsshell +// META: global=window,dedicatedworker,jsshell,shadowrealm test(() => { const argument = { parameters: [] }; diff --git a/test/js-api/tag/type.tentative.any.js b/test/js-api/tag/type.tentative.any.js deleted file mode 100644 index 9d2f0de1a..000000000 --- a/test/js-api/tag/type.tentative.any.js +++ /dev/null @@ -1,21 +0,0 @@ -// META: global=window,dedicatedworker,jsshell -// META: script=/wasm/jsapi/assertions.js - -function assert_type(argument) { - const tag = new WebAssembly.Tag(argument); - const tagtype = tag.type(); - - assert_array_equals(tagtype.parameters, argument.parameters); -} - -test(() => { - assert_type({ parameters: [] }); -}, "[]"); - -test(() => { - assert_type({ parameters: ["i32", "i64"] }); -}, "[i32 i64]"); - -test(() => { - assert_type({ parameters: ["i32", "i64", "f32", "f64"] }); -}, "[i32 i64 f32 f64]"); diff --git a/test/js-api/wasm-module-builder.js b/test/js-api/wasm-module-builder.js index 8c6519239..1ffb00cba 100644 --- a/test/js-api/wasm-module-builder.js +++ b/test/js-api/wasm-module-builder.js @@ -97,8 +97,8 @@ let kDeclFunctionImport = 0x02; let kDeclFunctionLocals = 0x04; let kDeclFunctionExport = 0x08; -// Local types -let kWasmStmt = 0x40; +// Value types and related +let kWasmVoid = 0x40; let kWasmI32 = 0x7f; let kWasmI64 = 0x7e; let kWasmF32 = 0x7d; @@ -118,28 +118,32 @@ let kWasmEqRef = -0x13; let kWasmI31Ref = -0x14; let kWasmStructRef = -0x15; let kWasmArrayRef = -0x16; +let kWasmExnRef = -0x17; +let kWasmNullExnRef = -0x0c; // Use the positive-byte versions inside function bodies. let kLeb128Mask = 0x7f; let kFuncRefCode = kWasmFuncRef & kLeb128Mask; let kAnyFuncCode = kFuncRefCode; // Alias named as in the JS API spec let kExternRefCode = kWasmExternRef & kLeb128Mask; +let kExnRefCode = kWasmExnRef & kLeb128Mask; let kAnyRefCode = kWasmAnyRef & kLeb128Mask; let kEqRefCode = kWasmEqRef & kLeb128Mask; let kI31RefCode = kWasmI31Ref & kLeb128Mask; let kNullExternRefCode = kWasmNullExternRef & kLeb128Mask; let kNullFuncRefCode = kWasmNullFuncRef & kLeb128Mask; +let kNullExnRefCode = kWasmNullExnRef & kLeb128Mask; let kStructRefCode = kWasmStructRef & kLeb128Mask; let kArrayRefCode = kWasmArrayRef & kLeb128Mask; let kNullRefCode = kWasmNullRef & kLeb128Mask; let kWasmRefNull = 0x63; let kWasmRef = 0x64; -function wasmRefNullType(heap_type) { - return {opcode: kWasmRefNull, heap_type: heap_type}; +function wasmRefNullType(heap_type, is_shared = false) { + return {opcode: kWasmRefNull, heap_type: heap_type, is_shared: is_shared}; } -function wasmRefType(heap_type) { - return {opcode: kWasmRef, heap_type: heap_type}; +function wasmRefType(heap_type, is_shared = false) { + return {opcode: kWasmRef, heap_type: heap_type, is_shared: is_shared}; } let kExternalFunction = 0; @@ -152,7 +156,7 @@ let kTableZero = 0; let kMemoryZero = 0; let kSegmentZero = 0; -let kTagAttribute = 0; +let kExceptionAttribute = 0; // Useful signatures let kSig_i_i = makeSig([kWasmI32], [kWasmI32]); @@ -232,10 +236,9 @@ let kExprIf = 0x04; let kExprElse = 0x05; let kExprTry = 0x06; let kExprCatch = 0x07; -let kExprCatchAll = 0x19; let kExprThrow = 0x08; let kExprRethrow = 0x09; -let kExprBrOnExn = 0x0a; +let kExprThrowRef = 0x0a; let kExprEnd = 0x0b; let kExprBr = 0x0c; let kExprBrIf = 0x0d; @@ -245,8 +248,10 @@ let kExprCallFunction = 0x10; let kExprCallIndirect = 0x11; let kExprReturnCall = 0x12; let kExprReturnCallIndirect = 0x13; +let kExprCatchAll = 0x19; let kExprDrop = 0x1a; let kExprSelect = 0x1b; +let kExprTryTable = 0x1f; let kExprLocalGet = 0x20; let kExprLocalSet = 0x21; let kExprLocalTee = 0x22; @@ -456,7 +461,7 @@ let kExprBrOnCast = 0x18; let kExprBrOnCastFail = 0x19; let kExprExternInternalize = 0x1a; let kExprExternExternalize = 0x1b; -let kExprI31New = 0x1c; +let kExprRefI31 = 0x1c; let kExprI31GetS = 0x1d; let kExprI31GetU = 0x1e; @@ -549,6 +554,12 @@ let kExprI32x4Eq = 0x2c; let kExprS1x4AllTrue = 0x75; let kExprF32x4Min = 0x9e; +// Exception handling with exnref. +let kCatchNoRef = 0x0; +let kCatchRef = 0x1; +let kCatchAllNoRef = 0x2; +let kCatchAllRef = 0x3; + class Binary { constructor() { this.length = 0; @@ -923,7 +934,7 @@ class WasmModuleBuilder { addTable(type, initial_size, max_size = undefined, init_expr = undefined) { if (type == kWasmI32 || type == kWasmI64 || type == kWasmF32 || - type == kWasmF64 || type == kWasmS128 || type == kWasmStmt) { + type == kWasmF64 || type == kWasmS128 || type == kWasmVoid) { throw new Error('Tables must be of a reference type'); } if (init_expr != undefined) checkExpr(init_expr); @@ -955,7 +966,7 @@ class WasmModuleBuilder { } let type_index = (typeof type) == "number" ? type : this.addType(type); this.imports.push({module: module, name: name, kind: kExternalFunction, - type: type_index}); + type_index: type_index}); return this.num_imported_funcs++; } @@ -1173,7 +1184,7 @@ class WasmModuleBuilder { section.emit_u32v(imp.initial); // initial if (has_max) section.emit_u32v(imp.maximum); // maximum } else if (imp.kind == kExternalTag) { - section.emit_u32v(kTagAttribute); + section.emit_u32v(kExceptionAttribute); section.emit_u32v(imp.type); } else { throw new Error("unknown/unsupported import kind " + imp.kind); @@ -1199,6 +1210,10 @@ class WasmModuleBuilder { binary.emit_section(kTableSectionCode, section => { section.emit_u32v(wasm.tables.length); for (let table of wasm.tables) { + if (table.has_init) { + section.emit_u8(0x40); // "has initializer" + section.emit_u8(0x00); // Reserved byte. + } section.emit_type(table.type); section.emit_u8(table.has_max); section.emit_u32v(table.initial_size); @@ -1245,7 +1260,7 @@ class WasmModuleBuilder { binary.emit_section(kTagSectionCode, section => { section.emit_u32v(wasm.tags.length); for (let type of wasm.tags) { - section.emit_u32v(kTagAttribute); + section.emit_u32v(kExceptionAttribute); section.emit_u32v(type); } }); diff --git a/test/legacy/exceptions/rethrow.wast b/test/legacy/exceptions/core/rethrow.wast similarity index 100% rename from test/legacy/exceptions/rethrow.wast rename to test/legacy/exceptions/core/rethrow.wast diff --git a/test/legacy/run.py b/test/legacy/exceptions/core/run.py similarity index 98% rename from test/legacy/run.py rename to test/legacy/exceptions/core/run.py index f727aeef3..b2f6c314b 100755 --- a/test/legacy/run.py +++ b/test/legacy/exceptions/core/run.py @@ -23,7 +23,7 @@ arguments = parser.parse_args() sys.argv = sys.argv[:1] -exceptions_test_files = glob.glob(os.path.join(inputDir, "exceptions", "*.wast")) +exceptions_test_files = glob.glob(os.path.join(inputDir, "*.wast")) wasmCommand = arguments.wasm jsCommand = arguments.js diff --git a/test/legacy/exceptions/throw.wast b/test/legacy/exceptions/core/throw.wast similarity index 100% rename from test/legacy/exceptions/throw.wast rename to test/legacy/exceptions/core/throw.wast diff --git a/test/legacy/exceptions/try_catch.wast b/test/legacy/exceptions/core/try_catch.wast similarity index 96% rename from test/legacy/exceptions/try_catch.wast rename to test/legacy/exceptions/core/try_catch.wast index 2a0e9ff62..07399f3a5 100644 --- a/test/legacy/exceptions/try_catch.wast +++ b/test/legacy/exceptions/core/try_catch.wast @@ -167,6 +167,14 @@ (catch $e0) ) ) + + (func (export "break-try-catch") + (try (do (br 0)) (catch $e0)) + ) + + (func (export "break-try-catch_all") + (try (do (br 0)) (catch_all)) + ) ) (assert_return (invoke "empty-catch")) @@ -211,6 +219,9 @@ (assert_exception (invoke "return-call-in-try-catch")) (assert_exception (invoke "return-call-indirect-in-try-catch")) +(assert_return (invoke "break-try-catch")) +(assert_return (invoke "break-try-catch_all")) + (module (func $imported-throw (import "test" "throw")) (tag $e0) diff --git a/test/legacy/exceptions/try_delegate.wast b/test/legacy/exceptions/core/try_delegate.wast similarity index 83% rename from test/legacy/exceptions/try_delegate.wast rename to test/legacy/exceptions/core/try_delegate.wast index aae3a301b..39ee09a2b 100644 --- a/test/legacy/exceptions/try_delegate.wast +++ b/test/legacy/exceptions/core/try_delegate.wast @@ -145,6 +145,46 @@ (catch $e0) ) ) + + (func (export "break-try-delegate") + (try (do (br 0)) (delegate 0)) + ) + + (func (export "break-and-call-throw") (result i32) + (try $outer (result i32) + (do + (try (result i32) + (do + (block $a + (try (do (br $a)) (delegate $outer)) + ) + (call $throw-void) + (i32.const 0) + ) + (catch $e0 (i32.const 1)) + ) + ) + (catch $e0 (i32.const 2)) + ) + ) + + (func (export "break-and-throw") (result i32) + (try $outer (result i32) + (do + (try (result i32) + (do + (block $a + (try (do (br $a)) (delegate $outer)) + ) + (throw $e0) + (i32.const 0) + ) + (catch $e0 (i32.const 1)) + ) + ) + (catch $e0 (i32.const 2)) + ) + ) ) (assert_return (invoke "delegate-no-throw") (i32.const 1)) @@ -173,6 +213,11 @@ (assert_exception (invoke "return-call-in-try-delegate")) (assert_exception (invoke "return-call-indirect-in-try-delegate")) +(assert_return (invoke "break-try-delegate")) + +(assert_return (invoke "break-and-call-throw") (i32.const 1)) +(assert_return (invoke "break-and-throw") (i32.const 1)) + (assert_malformed (module quote "(module (func (delegate 0)))") "unexpected token" diff --git a/test/legacy/exceptions/js-api/basic.tentative.any.js b/test/legacy/exceptions/js-api/basic.tentative.any.js new file mode 100644 index 000000000..81478529a --- /dev/null +++ b/test/legacy/exceptions/js-api/basic.tentative.any.js @@ -0,0 +1,120 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm +// META: script=/wasm/jsapi/wasm-module-builder.js + +function assert_throws_wasm(fn, message) { + try { + fn(); + assert_not_reached(`expected to throw with ${message}`); + } catch (e) { + assert_true(e instanceof WebAssembly.Exception, `Error should be a WebAssembly.Exception with ${message}`); + } +} + +promise_test(async () => { + const kSig_v_r = makeSig([kWasmExternRef], []); + const builder = new WasmModuleBuilder(); + const tagIndex = builder.addTag(kSig_v_r); + builder.addFunction("throw_param", kSig_v_r) + .addBody([ + kExprLocalGet, 0, + kExprThrow, tagIndex, + ]) + .exportFunc(); + const buffer = builder.toBuffer(); + const {instance} = await WebAssembly.instantiate(buffer, {}); + const values = [ + undefined, + null, + true, + false, + "test", + Symbol(), + 0, + 1, + 4.2, + NaN, + Infinity, + {}, + () => {}, + ]; + for (const v of values) { + assert_throws_wasm(() => instance.exports.throw_param(v), String(v)); + } +}, "Wasm function throws argument"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const tagIndex = builder.addTag(kSig_v_a); + builder.addFunction("throw_null", kSig_v_v) + .addBody([ + kExprRefNull, kAnyFuncCode, + kExprThrow, tagIndex, + ]) + .exportFunc(); + const buffer = builder.toBuffer(); + const {instance} = await WebAssembly.instantiate(buffer, {}); + assert_throws_wasm(() => instance.exports.throw_null()); +}, "Wasm function throws null"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const tagIndex = builder.addTag(kSig_v_i); + builder.addFunction("throw_int", kSig_v_v) + .addBody([ + ...wasmI32Const(7), + kExprThrow, tagIndex, + ]) + .exportFunc(); + const buffer = builder.toBuffer(); + const {instance} = await WebAssembly.instantiate(buffer, {}); + assert_throws_wasm(() => instance.exports.throw_int()); +}, "Wasm function throws integer"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const fnIndex = builder.addImport("module", "fn", kSig_v_v); + const tagIndex = builder.addTag(kSig_v_r); + builder.addFunction("catch_exception", kSig_r_v) + .addBody([ + kExprTry, kWasmVoid, + kExprCallFunction, fnIndex, + kExprCatch, tagIndex, + kExprReturn, + kExprEnd, + kExprRefNull, kExternRefCode, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const error = new Error(); + const fn = () => { throw error }; + const {instance} = await WebAssembly.instantiate(buffer, { + module: { fn } + }); + assert_throws_exactly(error, () => instance.exports.catch_exception()); +}, "Imported JS function throws"); + +promise_test(async () => { + const builder = new WasmModuleBuilder(); + const fnIndex = builder.addImport("module", "fn", kSig_v_v); + builder.addFunction("catch_and_rethrow", kSig_r_v) + .addBody([ + kExprTry, kWasmVoid, + kExprCallFunction, fnIndex, + kExprCatchAll, + kExprRethrow, 0x00, + kExprEnd, + kExprRefNull, kExternRefCode, + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + const error = new Error(); + const fn = () => { throw error }; + const {instance} = await WebAssembly.instantiate(buffer, { + module: { fn } + }); + assert_throws_exactly(error, () => instance.exports.catch_and_rethrow()); +}, "Imported JS function throws, Wasm catches and rethrows"); diff --git a/test/legacy/exceptions/js-api/identity.tentative.any.js b/test/legacy/exceptions/js-api/identity.tentative.any.js new file mode 100644 index 000000000..36651c7d5 --- /dev/null +++ b/test/legacy/exceptions/js-api/identity.tentative.any.js @@ -0,0 +1,170 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm +// META: script=/wasm/jsapi/assertions.js +// META: script=/wasm/jsapi/wasm-module-builder.js + +test(() => { + const builder = new WasmModuleBuilder(); + + // Tag defined in JavaScript and imported into Wasm + const jsTag = new WebAssembly.Tag({ parameters: ["i32"] }); + const jsTagIndex = builder.addImportedTag("module", "jsTag", kSig_v_i); + const jsTagExn = new WebAssembly.Exception(jsTag, [42]); + const jsTagExnSamePayload = new WebAssembly.Exception(jsTag, [42]); + const jsTagExnDiffPayload = new WebAssembly.Exception(jsTag, [53]); + const throwJSTagExnIndex = builder.addImport("module", "throwJSTagExn", kSig_v_v); + + // Tag defined in Wasm and exported to JS + const wasmTagIndex = builder.addTag(kSig_v_i); + builder.addExportOfKind("wasmTag", kExternalTag, wasmTagIndex); + const throwWasmTagExnIndex = builder.addImport("module", "throwWasmTagExn", kSig_v_v); + // Will be assigned after an instance is created + let wasmTagExn = null; + let wasmTagExnSamePayload = null; + let wasmTagExnDiffPayload = null; + + const imports = { + module: { + throwJSTagExn: function() { throw jsTagExn; }, + throwWasmTagExn: function() { throw wasmTagExn; }, + jsTag: jsTag + } + }; + + // Call a JS function that throws an exception using a JS-defined tag, catches + // it with a 'catch' instruction, and rethrows it. + builder + .addFunction("catch_js_tag_rethrow", kSig_v_v) + .addBody([ + kExprTry, kWasmVoid, + kExprCallFunction, throwJSTagExnIndex, + kExprCatch, jsTagIndex, + kExprDrop, + kExprRethrow, 0x00, + kExprEnd + ]) + .exportFunc(); + + // Call a JS function that throws an exception using a Wasm-defined tag, + // catches it with a 'catch' instruction, and rethrows it. + builder + .addFunction("catch_wasm_tag_rethrow", kSig_v_v) + .addBody([ + kExprTry, kWasmVoid, + kExprCallFunction, throwWasmTagExnIndex, + kExprCatch, wasmTagIndex, + kExprDrop, + kExprRethrow, 0x00, + kExprEnd + ]) + .exportFunc(); + + // Call a JS function that throws an exception using a JS-defined tag, catches + // it with a 'catch_all' instruction, and rethrows it. + builder + .addFunction("catch_all_js_tag_rethrow", kSig_v_v) + .addBody([ + kExprTry, kWasmVoid, + kExprCallFunction, throwJSTagExnIndex, + kExprCatchAll, + kExprRethrow, 0x00, + kExprEnd + ]) + .exportFunc(); + + // Call a JS function that throws an exception using a Wasm-defined tag, + // catches it with a 'catch_all' instruction, and rethrows it. + builder + .addFunction("catch_all_wasm_tag_rethrow", kSig_v_v) + .addBody([ + kExprTry, kWasmVoid, + kExprCallFunction, throwWasmTagExnIndex, + kExprCatchAll, + kExprRethrow, 0x00, + kExprEnd + ]) + .exportFunc(); + + // Call a JS function that throws an exception, catches it with a 'catch' + // instruction, and returns its i32 payload. + builder + .addFunction("catch_js_tag_return_payload", kSig_i_v) + .addBody([ + kExprTry, kWasmI32, + kExprCallFunction, throwJSTagExnIndex, + kExprI32Const, 0x00, + kExprCatch, jsTagIndex, + kExprReturn, + kExprEnd + ]) + .exportFunc(); + + // Call a JS function that throws an exception, catches it with a 'catch' + // instruction, and throws a new exception using that payload. + builder + .addFunction("catch_js_tag_throw_payload", kSig_v_v) + .addBody([ + kExprTry, kWasmVoid, + kExprCallFunction, throwJSTagExnIndex, + kExprCatch, jsTagIndex, + kExprThrow, jsTagIndex, + kExprEnd + ]) + .exportFunc(); + + const buffer = builder.toBuffer(); + + WebAssembly.instantiate(buffer, imports).then(result => { + // The exception object's identity should be preserved across 'rethrow's in + // Wasm code. Do tests with a tag defined in JS. + try { + result.instance.exports.catch_js_tag_rethrow(); + } catch (e) { + assert_equals(e, jsTagExn); + // Even if they have the same payload, they are different objects, so they + // shouldn't compare equal. + assert_not_equals(e, jsTagExnSamePayload); + assert_not_equals(e, jsTagExnDiffPayload); + } + try { + result.instance.exports.catch_all_js_tag_rethrow(); + } catch (e) { + assert_equals(e, jsTagExn); + assert_not_equals(e, jsTagExnSamePayload); + assert_not_equals(e, jsTagExnDiffPayload); + } + + // Do the same tests with a tag defined in Wasm. + const wasmTag = result.instance.exports.wasmTag; + wasmTagExn = new WebAssembly.Exception(wasmTag, [42]); + wasmTagExnSamePayload = new WebAssembly.Exception(wasmTag, [42]); + wasmTagExnDiffPayload = new WebAssembly.Exception(wasmTag, [53]); + try { + result.instance.exports.catch_wasm_tag_rethrow(); + } catch (e) { + assert_equals(e, wasmTagExn); + assert_not_equals(e, wasmTagExnSamePayload); + assert_not_equals(e, wasmTagExnDiffPayload); + } + try { + result.instance.exports.catch_all_wasm_tag_rethrow(); + } catch (e) { + assert_equals(e, wasmTagExn); + assert_not_equals(e, wasmTagExnSamePayload); + assert_not_equals(e, wasmTagExnDiffPayload); + } + + // This function catches the exception and returns its i32 payload, which + // should match the original payload. + assert_equals(result.instance.exports.catch_js_tag_return_payload(), 42); + + // This function catches the exception and throws a new exception using the + // its payload. Even if the payload is reused, the exception objects should + // not compare equal. + try { + result.instance.exports.catch_js_tag_throw_payload(); + } catch (e) { + assert_equals(e.getArg(jsTag, 0), 42); + assert_not_equals(e, jsTagExn); + } + }); +}, "Identity check"); diff --git a/test/meta/Makefile b/test/meta/Makefile index 20e9adb2f..05a42beb7 100644 --- a/test/meta/Makefile +++ b/test/meta/Makefile @@ -1,10 +1,10 @@ SHARED_MEM=false # SpiderMonkey shell -JSSHELL=~/mozilla-central/js/src/build-debug/dist/bin/js -e 'const WITH_SHARED_MEMORY=$(SHARED_MEM);' -f common.js +#JSSHELL=~/mozilla-central/js/src/build-debug/dist/bin/js -e 'const WITH_SHARED_MEMORY=$(SHARED_MEM);' -f common.js # Node.js -#JSSHELL=./noderun.sh $(SHARED_MEM) +JSSHELL=./noderun.sh $(SHARED_MEM) TARGETDIR=../core diff --git a/test/meta/common.js b/test/meta/common.js index 9a277e44f..493da2d47 100644 --- a/test/meta/common.js +++ b/test/meta/common.js @@ -7,23 +7,23 @@ function print_origin(origin) { print(";;"); } -function checkRangeCode() { +function checkRangeCode(memtype) { return ` - (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (func (export "checkRange") (param $from ${memtype}) (param $to ${memtype}) (param $expected i32) (result ${memtype}) (loop $cont - (if (i32.eq (local.get $from) (local.get $to)) + (if (${memtype}.eq (local.get $from) (local.get $to)) (then - (return (i32.const -1)))) + (return (${memtype}.const -1)))) (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) (then - (local.set $from (i32.add (local.get $from) (i32.const 1))) + (local.set $from (${memtype}.add (local.get $from) (${memtype}.const 1))) (br $cont)))) (return (local.get $from))) `; } -function checkRange(from, to, expected) { +function checkRange(memtype, from, to, expected) { print( -`(assert_return (invoke "checkRange" (i32.const ${from}) (i32.const ${to}) (i32.const ${expected})) - (i32.const -1))`); +`(assert_return (invoke "checkRange" (${memtype}.const ${from}) (${memtype}.const ${to}) (i32.const ${expected})) + (${memtype}.const -1))`); } diff --git a/test/meta/generate_memory_copy.js b/test/meta/generate_memory_copy.js index 36751b86a..ee4714354 100644 --- a/test/meta/generate_memory_copy.js +++ b/test/meta/generate_memory_copy.js @@ -3,96 +3,100 @@ print_origin("generate_memory_copy.js"); -// In-bounds tests. +for ( const memtype of ['i32', 'i64'] ) { -function mem_test(instruction, expected_result_vector) { - print( + const decltype = memtype == 'i64' ? ' i64' : ''; + + // In-bounds tests. + + function mem_test(instruction, expected_result_vector) { + print( ` (module - (memory (export "memory0") 1 1) - (data (i32.const 2) "\\03\\01\\04\\01") - (data (i32.const 12) "\\07\\05\\02\\03\\06") + (memory (export "memory0")${decltype} 1 1) + (data (${memtype}.const 2) "\\03\\01\\04\\01") + (data (${memtype}.const 12) "\\07\\05\\02\\03\\06") (func (export "test") ${instruction}) - (func (export "load8_u") (param i32) (result i32) + (func (export "load8_u") (param ${memtype}) (result i32) (i32.load8_u (local.get 0)))) (invoke "test") `); - for (let i = 0; i < expected_result_vector.length; i++) { - print(`(assert_return (invoke "load8_u" (i32.const ${i})) (i32.const ${expected_result_vector[i]}))`); - } -} - -const e = 0; - -// This just gives the initial state of the memory, with its active -// initialisers applied. -mem_test("(nop)", - [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); - -// Copy non-zero over non-zero -mem_test("(memory.copy (i32.const 13) (i32.const 2) (i32.const 3))", - [e,e,3,1,4, 1,e,e,e,e, e,e,7,3,1, 4,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); - -// Copy non-zero over zero -mem_test("(memory.copy (i32.const 25) (i32.const 15) (i32.const 2))", - [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, 3,6,e,e,e]); - -// Copy zero over non-zero -mem_test("(memory.copy (i32.const 13) (i32.const 25) (i32.const 3))", - [e,e,3,1,4, 1,e,e,e,e, e,e,7,e,e, e,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); - -// Copy zero over zero -mem_test("(memory.copy (i32.const 20) (i32.const 22) (i32.const 4))", - [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); - -// Copy zero and non-zero entries, non overlapping -mem_test("(memory.copy (i32.const 25) (i32.const 1) (i32.const 3))", - [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,3,1,e,e]); - -// Copy zero and non-zero entries, overlapping, backwards -mem_test("(memory.copy (i32.const 10) (i32.const 12) (i32.const 7))", - [e,e,3,1,4, 1,e,e,e,e, 7,5,2,3,6, e,e,e,e,e, e,e,e,e,e, e,e,e,e,e]); - -// Copy zero and non-zero entries, overlapping, forwards -mem_test("(memory.copy (i32.const 12) (i32.const 10) (i32.const 7))", - [e,e,3,1,4, 1,e,e,e,e, e,e,e,e,7, 5,2,3,6,e, e,e,e,e,e, e,e,e,e,e]); - -// Out-of-bounds tests. -// -// The operation is out of bounds of the memory for the source or target, but -// must perform the operation up to the appropriate bound. Major cases: -// -// - non-overlapping regions -// - overlapping regions with src >= dest -// - overlapping regions with src == dest -// - overlapping regions with src < dest -// - arithmetic overflow on src addresses -// - arithmetic overflow on target addresses -// -// for each of those, -// -// - src address oob -// - target address oob -// - both oob - -function initializers(count, startingAt) { - let s = ""; - for ( let i=0, j=startingAt; i < count; i++, j++ ) - s += "\\" + (i + 256).toString(16).substring(1); - return s; -} - -function mem_copy(min, max, shared, srcOffs, targetOffs, len) { - let copyDown = srcOffs < targetOffs; - let memLength = min * PAGESIZE; - let targetAvail = memLength - targetOffs; - let srcAvail = memLength - srcOffs; - let targetLim = targetOffs + Math.min(len, targetAvail, srcAvail); - let srcLim = srcOffs + Math.min(len, targetAvail, srcAvail); - - print( + for (let i = 0; i < expected_result_vector.length; i++) { + print(`(assert_return (invoke "load8_u" (${memtype}.const ${i})) (i32.const ${expected_result_vector[i]}))`); + } + } + + const e = 0; + + // This just gives the initial state of the memory, with its active + // initialisers applied. + mem_test("(nop)", + [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); + + // Copy non-zero over non-zero + mem_test(`(memory.copy (${memtype}.const 13) (${memtype}.const 2) (${memtype}.const 3))`, + [e,e,3,1,4, 1,e,e,e,e, e,e,7,3,1, 4,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); + + // Copy non-zero over zero + mem_test(`(memory.copy (${memtype}.const 25) (${memtype}.const 15) (${memtype}.const 2))`, + [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, 3,6,e,e,e]); + + // Copy zero over non-zero + mem_test(`(memory.copy (${memtype}.const 13) (${memtype}.const 25) (${memtype}.const 3))`, + [e,e,3,1,4, 1,e,e,e,e, e,e,7,e,e, e,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); + + // Copy zero over zero + mem_test(`(memory.copy (${memtype}.const 20) (${memtype}.const 22) (${memtype}.const 4))`, + [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); + + // Copy zero and non-zero entries, non overlapping + mem_test(`(memory.copy (${memtype}.const 25) (${memtype}.const 1) (${memtype}.const 3))`, + [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,3,1,e,e]); + + // Copy zero and non-zero entries, overlapping, backwards + mem_test(`(memory.copy (${memtype}.const 10) (${memtype}.const 12) (${memtype}.const 7))`, + [e,e,3,1,4, 1,e,e,e,e, 7,5,2,3,6, e,e,e,e,e, e,e,e,e,e, e,e,e,e,e]); + + // Copy zero and non-zero entries, overlapping, forwards + mem_test(`(memory.copy (${memtype}.const 12) (${memtype}.const 10) (${memtype}.const 7))`, + [e,e,3,1,4, 1,e,e,e,e, e,e,e,e,7, 5,2,3,6,e, e,e,e,e,e, e,e,e,e,e]); + + // Out-of-bounds tests. + // + // The operation is out of bounds of the memory for the source or target, but + // must perform the operation up to the appropriate bound. Major cases: + // + // - non-overlapping regions + // - overlapping regions with src >= dest + // - overlapping regions with src == dest + // - overlapping regions with src < dest + // - arithmetic overflow on src addresses + // - arithmetic overflow on target addresses + // + // for each of those, + // + // - src address oob + // - target address oob + // - both oob + + function initializers(count, startingAt) { + let s = ""; + for ( let i=0, j=startingAt; i < count; i++, j++ ) + s += "\\" + (i + 256).toString(16).substring(1); + return s; + } + + function mem_copy(min, max, shared, srcOffs, targetOffs, len) { + let copyDown = srcOffs < targetOffs; + let memLength = min * PAGESIZE; + let targetAvail = memLength - targetOffs; + let srcAvail = memLength - srcOffs; + let targetLim = targetOffs + Math.min(len, targetAvail, srcAvail); + let srcLim = srcOffs + Math.min(len, targetAvail, srcAvail); + + print( ` (module (memory (export "mem") ${min} ${max} ${shared}) @@ -106,663 +110,664 @@ function mem_copy(min, max, shared, srcOffs, targetOffs, len) { "out of bounds memory access") `); - let immediateOOB = copyDown && (srcOffs + len > memLength || targetOffs + len > memLength); - - var s = 0; - var i = 0; - let k = 0; - for (i=0; i < memLength; i++ ) { - if (i >= srcOffs && i < srcLim) { - print(`(assert_return (invoke "load8_u" (i32.const ${i})) (i32.const ${(s++) & 0xFF}))`); - continue; - } - // Only spot-check for zero, or we'll be here all night. - if (++k == 199) { - print(`(assert_return (invoke "load8_u" (i32.const ${i})) (i32.const 0))`); - k = 0; - } - } -} - -// OOB target address, nonoverlapping -mem_copy(1, 1, "", 0, PAGESIZE-20, 40); -mem_copy(1, 1, "", 0, PAGESIZE-21, 39); -if (WITH_SHARED_MEMORY) { - mem_copy(2, 4, "shared", 0, 2*PAGESIZE-20, 40); - mem_copy(2, 4, "shared", 0, 2*PAGESIZE-21, 39); -} - -// OOB source address, nonoverlapping -mem_copy(1, 1, "", PAGESIZE-20, 0, 40); -mem_copy(1, 1, "", PAGESIZE-21, 0, 39); -if (WITH_SHARED_MEMORY) { - mem_copy(2, 4, "shared", 2*PAGESIZE-20, 0, 40); - mem_copy(2, 4, "shared", 2*PAGESIZE-21, 0, 39); -} - -// OOB target address, overlapping, src < target -mem_copy(1, 1, "", PAGESIZE-50, PAGESIZE-20, 40); - -// OOB source address, overlapping, target < src -mem_copy(1, 1, "", PAGESIZE-20, PAGESIZE-50, 40); - -// OOB both, overlapping, including target == src -mem_copy(1, 1, "", PAGESIZE-30, PAGESIZE-20, 40); -mem_copy(1, 1, "", PAGESIZE-20, PAGESIZE-30, 40); -mem_copy(1, 1, "", PAGESIZE-20, PAGESIZE-20, 40); - -// Arithmetic overflow on source address. -mem_copy(1, "", "", PAGESIZE-20, 0, 0xFFFFF000); - -// Arithmetic overflow on target adddress is an overlapping case. -mem_copy(1, 1, "", PAGESIZE-0x1000, PAGESIZE-20, 0xFFFFFF00); - -// Sundry compilation failures. - -// Module doesn't have a memory. -print( + let immediateOOB = copyDown && (srcOffs + len > memLength || targetOffs + len > memLength); + + var s = 0; + var i = 0; + let k = 0; + for (i=0; i < memLength; i++ ) { + if (i >= srcOffs && i < srcLim) { + print(`(assert_return (invoke "load8_u" (i32.const ${i})) (i32.const ${(s++) & 0xFF}))`); + continue; + } + // Only spot-check for zero, or we'll be here all night. + if (++k == 199) { + print(`(assert_return (invoke "load8_u" (i32.const ${i})) (i32.const 0))`); + k = 0; + } + } + } + + // OOB target address, nonoverlapping + mem_copy(1, 1, "", 0, PAGESIZE-20, 40); + mem_copy(1, 1, "", 0, PAGESIZE-21, 39); + if (WITH_SHARED_MEMORY) { + mem_copy(2, 4, "shared", 0, 2*PAGESIZE-20, 40); + mem_copy(2, 4, "shared", 0, 2*PAGESIZE-21, 39); + } + + // OOB source address, nonoverlapping + mem_copy(1, 1, "", PAGESIZE-20, 0, 40); + mem_copy(1, 1, "", PAGESIZE-21, 0, 39); + if (WITH_SHARED_MEMORY) { + mem_copy(2, 4, "shared", 2*PAGESIZE-20, 0, 40); + mem_copy(2, 4, "shared", 2*PAGESIZE-21, 0, 39); + } + + // OOB target address, overlapping, src < target + mem_copy(1, 1, "", PAGESIZE-50, PAGESIZE-20, 40); + + // OOB source address, overlapping, target < src + mem_copy(1, 1, "", PAGESIZE-20, PAGESIZE-50, 40); + + // OOB both, overlapping, including target == src + mem_copy(1, 1, "", PAGESIZE-30, PAGESIZE-20, 40); + mem_copy(1, 1, "", PAGESIZE-20, PAGESIZE-30, 40); + mem_copy(1, 1, "", PAGESIZE-20, PAGESIZE-20, 40); + + // Arithmetic overflow on source address. + mem_copy(1, "", "", PAGESIZE-20, 0, 0xFFFFF000); + + // Arithmetic overflow on target adddress is an overlapping case. + mem_copy(1, 1, "", PAGESIZE-0x1000, PAGESIZE-20, 0xFFFFFF00); + + // Sundry compilation failures. + + // Module doesn't have a memory. + print( ` (assert_invalid (module (func (export "testfn") - (memory.copy (i32.const 10) (i32.const 20) (i32.const 30)))) + (memory.copy (${memtype}.const 10) (${memtype}.const 20) (${memtype}.const 30)))) "unknown memory 0") `); -// Invalid argument types. TODO: We can add anyref, funcref, etc here. -{ - const tys = ['i32', 'f32', 'i64', 'f64']; - for (let ty1 of tys) { - for (let ty2 of tys) { - for (let ty3 of tys) { - if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32') - continue; // this is the only valid case - print( + // Invalid argument types. TODO: We can add anyref, funcref, etc here. + { + const tys = ['i32', 'f32', 'i64', 'f64']; + for (let ty1 of tys) { + for (let ty2 of tys) { + for (let ty3 of tys) { + if (ty1 == memtype && ty2 == memtype && ty3 == memtype) + continue; // this is the only valid case + print( `(assert_invalid (module - (memory 1 1) + (memory${decltype} 1 1) (func (export "testfn") (memory.copy (${ty1}.const 10) (${ty2}.const 20) (${ty3}.const 30)))) "type mismatch") `); - }}} -} + }}} + } -// Both ranges valid. Copy 5 bytes backwards by 1 (overlapping). -// result = 0x00--(09) 0x55--(11) 0x00--(pagesize-20) -print( + // Both ranges valid. Copy 5 bytes backwards by 1 (overlapping). + // result = 0x00--(09) 0x55--(11) 0x00--(pagesize-20) + print( ` (module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.fill (i32.const 10) (i32.const 0x55) (i32.const 10)) - (memory.copy (i32.const 9) (i32.const 10) (i32.const 5))) - ${checkRangeCode()}) + (memory.fill (${memtype}.const 10) (i32.const 0x55) (${memtype}.const 10)) + (memory.copy (${memtype}.const 9) (${memtype}.const 10) (${memtype}.const 5))) + ${checkRangeCode(memtype)}) (invoke "test") `); -checkRange(0, 0+9, 0x00); -checkRange(9, 9+11, 0x55); -checkRange(9+11, 0x10000, 0x00); + checkRange(memtype, 0, 0+9, 0x00); + checkRange(memtype, 9, 9+11, 0x55); + checkRange(memtype, 9+11, 0x10000, 0x00); -// Both ranges valid. Copy 5 bytes forwards by 1 (overlapping). -// result = 0x00--(10) 0x55--(11) 0x00--(pagesize-19) -print( + // Both ranges valid. Copy 5 bytes forwards by 1 (overlapping). + // result = 0x00--(10) 0x55--(11) 0x00--(pagesize-19) + print( ` (module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.fill (i32.const 10) (i32.const 0x55) (i32.const 10)) - (memory.copy (i32.const 16) (i32.const 15) (i32.const 5))) - ${checkRangeCode()}) + (memory.fill (${memtype}.const 10) (i32.const 0x55) (${memtype}.const 10)) + (memory.copy (${memtype}.const 16) (${memtype}.const 15) (${memtype}.const 5))) + ${checkRangeCode(memtype)}) (invoke "test") `); -checkRange(0, 0+10, 0x00); -checkRange(10, 10+11, 0x55); -checkRange(10+11, 0x10000, 0x00); + checkRange(memtype, 0, 0+10, 0x00); + checkRange(memtype, 10, 10+11, 0x55); + checkRange(memtype, 10+11, 0x10000, 0x00); -// Destination range invalid -print( + // Destination range invalid + print( ` (module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.copy (i32.const 0xFF00) (i32.const 0x8000) (i32.const 257)))) + (memory.copy (${memtype}.const 0xFF00) (${memtype}.const 0x8000) (${memtype}.const 257)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// Destination wraparound the end of 32-bit offset space + // Destination wraparound the end of 32-bit offset space print( `(module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.copy (i32.const 0xFFFFFF00) (i32.const 0x4000) (i32.const 257)))) + (memory.copy (${memtype}.const 0xFFFFFF00) (${memtype}.const 0x4000) (${memtype}.const 257)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// Source range invalid + // Source range invalid print( `(module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.copy (i32.const 0x8000) (i32.const 0xFF00) (i32.const 257)))) + (memory.copy (${memtype}.const 0x8000) (${memtype}.const 0xFF00) (${memtype}.const 257)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// Source wraparound the end of 32-bit offset space + // Source wraparound the end of 32-bit offset space print( `(module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.copy (i32.const 0x4000) (i32.const 0xFFFFFF00) (i32.const 257)))) + (memory.copy (${memtype}.const 0x4000) (${memtype}.const 0xFFFFFF00) (${memtype}.const 257)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// Zero len with both offsets in-bounds is a no-op + // Zero len with both offsets in-bounds is a no-op print( `(module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.fill (i32.const 0x0000) (i32.const 0x55) (i32.const 0x8000)) - (memory.fill (i32.const 0x8000) (i32.const 0xAA) (i32.const 0x8000)) - (memory.copy (i32.const 0x9000) (i32.const 0x7000) (i32.const 0))) - ${checkRangeCode()}) + (memory.fill (${memtype}.const 0x0000) (i32.const 0x55) (${memtype}.const 0x8000)) + (memory.fill (${memtype}.const 0x8000) (i32.const 0xAA) (${memtype}.const 0x8000)) + (memory.copy (${memtype}.const 0x9000) (${memtype}.const 0x7000) (${memtype}.const 0))) + ${checkRangeCode(memtype)}) (invoke "test") `); -checkRange(0x00000, 0x08000, 0x55); -checkRange(0x08000, 0x10000, 0xAA); + checkRange(memtype, 0x00000, 0x08000, 0x55); + checkRange(memtype, 0x08000, 0x10000, 0xAA); -// Zero len with dest offset out-of-bounds at the end of memory is allowed -print( + // Zero len with dest offset out-of-bounds at the end of memory is allowed + print( `(module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.copy (i32.const 0x10000) (i32.const 0x7000) (i32.const 0)))) + (memory.copy (${memtype}.const 0x10000) (${memtype}.const 0x7000) (${memtype}.const 0)))) (invoke "test") `); -// Zero len with dest offset out-of-bounds past the end of memory is not allowed -print( + // Zero len with dest offset out-of-bounds past the end of memory is not allowed + print( `(module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.copy (i32.const 0x20000) (i32.const 0x7000) (i32.const 0)))) + (memory.copy (${memtype}.const 0x20000) (${memtype}.const 0x7000) (${memtype}.const 0)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// Zero len with src offset out-of-bounds at the end of memory is allowed -print( + // Zero len with src offset out-of-bounds at the end of memory is allowed + print( `(module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.copy (i32.const 0x9000) (i32.const 0x10000) (i32.const 0)))) + (memory.copy (${memtype}.const 0x9000) (${memtype}.const 0x10000) (${memtype}.const 0)))) (invoke "test") `); -// Zero len with src offset out-of-bounds past the end of memory is not allowed -print( + // Zero len with src offset out-of-bounds past the end of memory is not allowed + print( `(module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.copy (i32.const 0x9000) (i32.const 0x20000) (i32.const 0)))) + (memory.copy (${memtype}.const 0x9000) (${memtype}.const 0x20000) (${memtype}.const 0)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// Zero len with both dest and src offsets out-of-bounds at the end of memory is allowed -print( + // Zero len with both dest and src offsets out-of-bounds at the end of memory is allowed + print( `(module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.copy (i32.const 0x10000) (i32.const 0x10000) (i32.const 0)))) + (memory.copy (${memtype}.const 0x10000) (${memtype}.const 0x10000) (${memtype}.const 0)))) (invoke "test") `); -// Zero len with both dest and src offsets out-of-bounds past the end of memory is not allowed -print( + // Zero len with both dest and src offsets out-of-bounds past the end of memory is not allowed + print( `(module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.copy (i32.const 0x20000) (i32.const 0x20000) (i32.const 0)))) + (memory.copy (${memtype}.const 0x20000) (${memtype}.const 0x20000) (${memtype}.const 0)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// 100 random fills followed by 100 random copies, in a single-page buffer, -// followed by verification of the (now heavily mashed-around) buffer. -print( + // 100 random fills followed by 100 random copies, in a single-page buffer, + // followed by verification of the (now heavily mashed-around) buffer. + print( `(module - (memory 1 1) + (memory${decltype} 1 1) (func (export "test") - (memory.fill (i32.const 17767) (i32.const 1) (i32.const 1344)) - (memory.fill (i32.const 39017) (i32.const 2) (i32.const 1055)) - (memory.fill (i32.const 56401) (i32.const 3) (i32.const 988)) - (memory.fill (i32.const 37962) (i32.const 4) (i32.const 322)) - (memory.fill (i32.const 7977) (i32.const 5) (i32.const 1994)) - (memory.fill (i32.const 22714) (i32.const 6) (i32.const 3036)) - (memory.fill (i32.const 16882) (i32.const 7) (i32.const 2372)) - (memory.fill (i32.const 43491) (i32.const 8) (i32.const 835)) - (memory.fill (i32.const 124) (i32.const 9) (i32.const 1393)) - (memory.fill (i32.const 2132) (i32.const 10) (i32.const 2758)) - (memory.fill (i32.const 8987) (i32.const 11) (i32.const 3098)) - (memory.fill (i32.const 52711) (i32.const 12) (i32.const 741)) - (memory.fill (i32.const 3958) (i32.const 13) (i32.const 2823)) - (memory.fill (i32.const 49715) (i32.const 14) (i32.const 1280)) - (memory.fill (i32.const 50377) (i32.const 15) (i32.const 1466)) - (memory.fill (i32.const 20493) (i32.const 16) (i32.const 3158)) - (memory.fill (i32.const 47665) (i32.const 17) (i32.const 544)) - (memory.fill (i32.const 12451) (i32.const 18) (i32.const 2669)) - (memory.fill (i32.const 24869) (i32.const 19) (i32.const 2651)) - (memory.fill (i32.const 45317) (i32.const 20) (i32.const 1570)) - (memory.fill (i32.const 43096) (i32.const 21) (i32.const 1691)) - (memory.fill (i32.const 33886) (i32.const 22) (i32.const 646)) - (memory.fill (i32.const 48555) (i32.const 23) (i32.const 1858)) - (memory.fill (i32.const 53453) (i32.const 24) (i32.const 2657)) - (memory.fill (i32.const 30363) (i32.const 25) (i32.const 981)) - (memory.fill (i32.const 9300) (i32.const 26) (i32.const 1807)) - (memory.fill (i32.const 50190) (i32.const 27) (i32.const 487)) - (memory.fill (i32.const 62753) (i32.const 28) (i32.const 530)) - (memory.fill (i32.const 36316) (i32.const 29) (i32.const 943)) - (memory.fill (i32.const 6768) (i32.const 30) (i32.const 381)) - (memory.fill (i32.const 51262) (i32.const 31) (i32.const 3089)) - (memory.fill (i32.const 49729) (i32.const 32) (i32.const 658)) - (memory.fill (i32.const 44540) (i32.const 33) (i32.const 1702)) - (memory.fill (i32.const 33342) (i32.const 34) (i32.const 1092)) - (memory.fill (i32.const 50814) (i32.const 35) (i32.const 1410)) - (memory.fill (i32.const 47594) (i32.const 36) (i32.const 2204)) - (memory.fill (i32.const 54123) (i32.const 37) (i32.const 2394)) - (memory.fill (i32.const 55183) (i32.const 38) (i32.const 250)) - (memory.fill (i32.const 22620) (i32.const 39) (i32.const 2097)) - (memory.fill (i32.const 17132) (i32.const 40) (i32.const 3264)) - (memory.fill (i32.const 54331) (i32.const 41) (i32.const 3299)) - (memory.fill (i32.const 39474) (i32.const 42) (i32.const 2796)) - (memory.fill (i32.const 36156) (i32.const 43) (i32.const 2070)) - (memory.fill (i32.const 35308) (i32.const 44) (i32.const 2763)) - (memory.fill (i32.const 32731) (i32.const 45) (i32.const 312)) - (memory.fill (i32.const 63746) (i32.const 46) (i32.const 192)) - (memory.fill (i32.const 30974) (i32.const 47) (i32.const 596)) - (memory.fill (i32.const 16635) (i32.const 48) (i32.const 501)) - (memory.fill (i32.const 57002) (i32.const 49) (i32.const 686)) - (memory.fill (i32.const 34299) (i32.const 50) (i32.const 385)) - (memory.fill (i32.const 60881) (i32.const 51) (i32.const 903)) - (memory.fill (i32.const 61445) (i32.const 52) (i32.const 2390)) - (memory.fill (i32.const 46972) (i32.const 53) (i32.const 1441)) - (memory.fill (i32.const 25973) (i32.const 54) (i32.const 3162)) - (memory.fill (i32.const 5566) (i32.const 55) (i32.const 2135)) - (memory.fill (i32.const 35977) (i32.const 56) (i32.const 519)) - (memory.fill (i32.const 44892) (i32.const 57) (i32.const 3280)) - (memory.fill (i32.const 46760) (i32.const 58) (i32.const 1678)) - (memory.fill (i32.const 46607) (i32.const 59) (i32.const 3168)) - (memory.fill (i32.const 22449) (i32.const 60) (i32.const 1441)) - (memory.fill (i32.const 58609) (i32.const 61) (i32.const 663)) - (memory.fill (i32.const 32261) (i32.const 62) (i32.const 1671)) - (memory.fill (i32.const 3063) (i32.const 63) (i32.const 721)) - (memory.fill (i32.const 34025) (i32.const 64) (i32.const 84)) - (memory.fill (i32.const 33338) (i32.const 65) (i32.const 2029)) - (memory.fill (i32.const 36810) (i32.const 66) (i32.const 29)) - (memory.fill (i32.const 19147) (i32.const 67) (i32.const 3034)) - (memory.fill (i32.const 12616) (i32.const 68) (i32.const 1043)) - (memory.fill (i32.const 18276) (i32.const 69) (i32.const 3324)) - (memory.fill (i32.const 4639) (i32.const 70) (i32.const 1091)) - (memory.fill (i32.const 16158) (i32.const 71) (i32.const 1997)) - (memory.fill (i32.const 18204) (i32.const 72) (i32.const 2259)) - (memory.fill (i32.const 50532) (i32.const 73) (i32.const 3189)) - (memory.fill (i32.const 11028) (i32.const 74) (i32.const 1968)) - (memory.fill (i32.const 15962) (i32.const 75) (i32.const 1455)) - (memory.fill (i32.const 45406) (i32.const 76) (i32.const 1177)) - (memory.fill (i32.const 54137) (i32.const 77) (i32.const 1568)) - (memory.fill (i32.const 33083) (i32.const 78) (i32.const 1642)) - (memory.fill (i32.const 61028) (i32.const 79) (i32.const 3284)) - (memory.fill (i32.const 51729) (i32.const 80) (i32.const 223)) - (memory.fill (i32.const 4361) (i32.const 81) (i32.const 2171)) - (memory.fill (i32.const 57514) (i32.const 82) (i32.const 1322)) - (memory.fill (i32.const 55724) (i32.const 83) (i32.const 2648)) - (memory.fill (i32.const 24091) (i32.const 84) (i32.const 1045)) - (memory.fill (i32.const 43183) (i32.const 85) (i32.const 3097)) - (memory.fill (i32.const 32307) (i32.const 86) (i32.const 2796)) - (memory.fill (i32.const 3811) (i32.const 87) (i32.const 2010)) - (memory.fill (i32.const 54856) (i32.const 88) (i32.const 0)) - (memory.fill (i32.const 49941) (i32.const 89) (i32.const 2069)) - (memory.fill (i32.const 20411) (i32.const 90) (i32.const 2896)) - (memory.fill (i32.const 33826) (i32.const 91) (i32.const 192)) - (memory.fill (i32.const 9402) (i32.const 92) (i32.const 2195)) - (memory.fill (i32.const 12413) (i32.const 93) (i32.const 24)) - (memory.fill (i32.const 14091) (i32.const 94) (i32.const 577)) - (memory.fill (i32.const 44058) (i32.const 95) (i32.const 2089)) - (memory.fill (i32.const 36735) (i32.const 96) (i32.const 3436)) - (memory.fill (i32.const 23288) (i32.const 97) (i32.const 2765)) - (memory.fill (i32.const 6392) (i32.const 98) (i32.const 830)) - (memory.fill (i32.const 33307) (i32.const 99) (i32.const 1938)) - (memory.fill (i32.const 21941) (i32.const 100) (i32.const 2750)) - (memory.copy (i32.const 59214) (i32.const 54248) (i32.const 2098)) - (memory.copy (i32.const 63026) (i32.const 39224) (i32.const 230)) - (memory.copy (i32.const 51833) (i32.const 23629) (i32.const 2300)) - (memory.copy (i32.const 6708) (i32.const 23996) (i32.const 639)) - (memory.copy (i32.const 6990) (i32.const 33399) (i32.const 1097)) - (memory.copy (i32.const 19403) (i32.const 10348) (i32.const 3197)) - (memory.copy (i32.const 27308) (i32.const 54406) (i32.const 100)) - (memory.copy (i32.const 27221) (i32.const 43682) (i32.const 1717)) - (memory.copy (i32.const 60528) (i32.const 8629) (i32.const 119)) - (memory.copy (i32.const 5947) (i32.const 2308) (i32.const 658)) - (memory.copy (i32.const 4787) (i32.const 51631) (i32.const 2269)) - (memory.copy (i32.const 12617) (i32.const 19197) (i32.const 833)) - (memory.copy (i32.const 11854) (i32.const 46505) (i32.const 3300)) - (memory.copy (i32.const 11376) (i32.const 45012) (i32.const 2281)) - (memory.copy (i32.const 34186) (i32.const 6697) (i32.const 2572)) - (memory.copy (i32.const 4936) (i32.const 1690) (i32.const 1328)) - (memory.copy (i32.const 63164) (i32.const 7637) (i32.const 1670)) - (memory.copy (i32.const 44568) (i32.const 18344) (i32.const 33)) - (memory.copy (i32.const 43918) (i32.const 22348) (i32.const 1427)) - (memory.copy (i32.const 46637) (i32.const 49819) (i32.const 1434)) - (memory.copy (i32.const 63684) (i32.const 8755) (i32.const 834)) - (memory.copy (i32.const 33485) (i32.const 20131) (i32.const 3317)) - (memory.copy (i32.const 40575) (i32.const 54317) (i32.const 3201)) - (memory.copy (i32.const 25812) (i32.const 59254) (i32.const 2452)) - (memory.copy (i32.const 19678) (i32.const 56882) (i32.const 346)) - (memory.copy (i32.const 15852) (i32.const 35914) (i32.const 2430)) - (memory.copy (i32.const 11824) (i32.const 35574) (i32.const 300)) - (memory.copy (i32.const 59427) (i32.const 13957) (i32.const 3153)) - (memory.copy (i32.const 34299) (i32.const 60594) (i32.const 1281)) - (memory.copy (i32.const 8964) (i32.const 12276) (i32.const 943)) - (memory.copy (i32.const 2827) (i32.const 10425) (i32.const 1887)) - (memory.copy (i32.const 43194) (i32.const 43910) (i32.const 738)) - (memory.copy (i32.const 63038) (i32.const 18949) (i32.const 122)) - (memory.copy (i32.const 24044) (i32.const 44761) (i32.const 1755)) - (memory.copy (i32.const 22608) (i32.const 14755) (i32.const 702)) - (memory.copy (i32.const 11284) (i32.const 26579) (i32.const 1830)) - (memory.copy (i32.const 23092) (i32.const 20471) (i32.const 1064)) - (memory.copy (i32.const 57248) (i32.const 54770) (i32.const 2631)) - (memory.copy (i32.const 25492) (i32.const 1025) (i32.const 3113)) - (memory.copy (i32.const 49588) (i32.const 44220) (i32.const 975)) - (memory.copy (i32.const 28280) (i32.const 41722) (i32.const 2336)) - (memory.copy (i32.const 61289) (i32.const 230) (i32.const 2872)) - (memory.copy (i32.const 22480) (i32.const 52506) (i32.const 2197)) - (memory.copy (i32.const 40553) (i32.const 9578) (i32.const 1958)) - (memory.copy (i32.const 29004) (i32.const 20862) (i32.const 2186)) - (memory.copy (i32.const 53029) (i32.const 43955) (i32.const 1037)) - (memory.copy (i32.const 25476) (i32.const 35667) (i32.const 1650)) - (memory.copy (i32.const 58516) (i32.const 45819) (i32.const 1986)) - (memory.copy (i32.const 38297) (i32.const 5776) (i32.const 1955)) - (memory.copy (i32.const 28503) (i32.const 55364) (i32.const 2368)) - (memory.copy (i32.const 62619) (i32.const 18108) (i32.const 1356)) - (memory.copy (i32.const 50149) (i32.const 13861) (i32.const 382)) - (memory.copy (i32.const 16904) (i32.const 36341) (i32.const 1900)) - (memory.copy (i32.const 48098) (i32.const 11358) (i32.const 2807)) - (memory.copy (i32.const 28512) (i32.const 40362) (i32.const 323)) - (memory.copy (i32.const 35506) (i32.const 27856) (i32.const 1670)) - (memory.copy (i32.const 62970) (i32.const 53332) (i32.const 1341)) - (memory.copy (i32.const 14133) (i32.const 46312) (i32.const 644)) - (memory.copy (i32.const 29030) (i32.const 19074) (i32.const 496)) - (memory.copy (i32.const 44952) (i32.const 47577) (i32.const 2784)) - (memory.copy (i32.const 39559) (i32.const 44661) (i32.const 1350)) - (memory.copy (i32.const 10352) (i32.const 29274) (i32.const 1475)) - (memory.copy (i32.const 46911) (i32.const 46178) (i32.const 1467)) - (memory.copy (i32.const 4905) (i32.const 28740) (i32.const 1895)) - (memory.copy (i32.const 38012) (i32.const 57253) (i32.const 1751)) - (memory.copy (i32.const 26446) (i32.const 27223) (i32.const 1127)) - (memory.copy (i32.const 58835) (i32.const 24657) (i32.const 1063)) - (memory.copy (i32.const 61356) (i32.const 38790) (i32.const 766)) - (memory.copy (i32.const 44160) (i32.const 2284) (i32.const 1520)) - (memory.copy (i32.const 32740) (i32.const 47237) (i32.const 3014)) - (memory.copy (i32.const 11148) (i32.const 21260) (i32.const 1011)) - (memory.copy (i32.const 7665) (i32.const 31612) (i32.const 3034)) - (memory.copy (i32.const 18044) (i32.const 12987) (i32.const 3320)) - (memory.copy (i32.const 57306) (i32.const 55905) (i32.const 308)) - (memory.copy (i32.const 24675) (i32.const 16815) (i32.const 1155)) - (memory.copy (i32.const 19900) (i32.const 10115) (i32.const 722)) - (memory.copy (i32.const 2921) (i32.const 5935) (i32.const 2370)) - (memory.copy (i32.const 32255) (i32.const 50095) (i32.const 2926)) - (memory.copy (i32.const 15126) (i32.const 17299) (i32.const 2607)) - (memory.copy (i32.const 45575) (i32.const 28447) (i32.const 2045)) - (memory.copy (i32.const 55149) (i32.const 36113) (i32.const 2596)) - (memory.copy (i32.const 28461) (i32.const 54157) (i32.const 1168)) - (memory.copy (i32.const 47951) (i32.const 53385) (i32.const 3137)) - (memory.copy (i32.const 30646) (i32.const 45155) (i32.const 2649)) - (memory.copy (i32.const 5057) (i32.const 4295) (i32.const 52)) - (memory.copy (i32.const 6692) (i32.const 24195) (i32.const 441)) - (memory.copy (i32.const 32984) (i32.const 27117) (i32.const 3445)) - (memory.copy (i32.const 32530) (i32.const 59372) (i32.const 2785)) - (memory.copy (i32.const 34361) (i32.const 8962) (i32.const 2406)) - (memory.copy (i32.const 17893) (i32.const 54538) (i32.const 3381)) - (memory.copy (i32.const 22685) (i32.const 44151) (i32.const 136)) - (memory.copy (i32.const 59089) (i32.const 7077) (i32.const 1045)) - (memory.copy (i32.const 42945) (i32.const 55028) (i32.const 2389)) - (memory.copy (i32.const 44693) (i32.const 20138) (i32.const 877)) - (memory.copy (i32.const 36810) (i32.const 25196) (i32.const 3447)) - (memory.copy (i32.const 45742) (i32.const 31888) (i32.const 854)) - (memory.copy (i32.const 24236) (i32.const 31866) (i32.const 1377)) - (memory.copy (i32.const 33778) (i32.const 692) (i32.const 1594)) - (memory.copy (i32.const 60618) (i32.const 18585) (i32.const 2987)) - (memory.copy (i32.const 50370) (i32.const 41271) (i32.const 1406)) + (memory.fill (${memtype}.const 17767) (i32.const 1) (${memtype}.const 1344)) + (memory.fill (${memtype}.const 39017) (i32.const 2) (${memtype}.const 1055)) + (memory.fill (${memtype}.const 56401) (i32.const 3) (${memtype}.const 988)) + (memory.fill (${memtype}.const 37962) (i32.const 4) (${memtype}.const 322)) + (memory.fill (${memtype}.const 7977) (i32.const 5) (${memtype}.const 1994)) + (memory.fill (${memtype}.const 22714) (i32.const 6) (${memtype}.const 3036)) + (memory.fill (${memtype}.const 16882) (i32.const 7) (${memtype}.const 2372)) + (memory.fill (${memtype}.const 43491) (i32.const 8) (${memtype}.const 835)) + (memory.fill (${memtype}.const 124) (i32.const 9) (${memtype}.const 1393)) + (memory.fill (${memtype}.const 2132) (i32.const 10) (${memtype}.const 2758)) + (memory.fill (${memtype}.const 8987) (i32.const 11) (${memtype}.const 3098)) + (memory.fill (${memtype}.const 52711) (i32.const 12) (${memtype}.const 741)) + (memory.fill (${memtype}.const 3958) (i32.const 13) (${memtype}.const 2823)) + (memory.fill (${memtype}.const 49715) (i32.const 14) (${memtype}.const 1280)) + (memory.fill (${memtype}.const 50377) (i32.const 15) (${memtype}.const 1466)) + (memory.fill (${memtype}.const 20493) (i32.const 16) (${memtype}.const 3158)) + (memory.fill (${memtype}.const 47665) (i32.const 17) (${memtype}.const 544)) + (memory.fill (${memtype}.const 12451) (i32.const 18) (${memtype}.const 2669)) + (memory.fill (${memtype}.const 24869) (i32.const 19) (${memtype}.const 2651)) + (memory.fill (${memtype}.const 45317) (i32.const 20) (${memtype}.const 1570)) + (memory.fill (${memtype}.const 43096) (i32.const 21) (${memtype}.const 1691)) + (memory.fill (${memtype}.const 33886) (i32.const 22) (${memtype}.const 646)) + (memory.fill (${memtype}.const 48555) (i32.const 23) (${memtype}.const 1858)) + (memory.fill (${memtype}.const 53453) (i32.const 24) (${memtype}.const 2657)) + (memory.fill (${memtype}.const 30363) (i32.const 25) (${memtype}.const 981)) + (memory.fill (${memtype}.const 9300) (i32.const 26) (${memtype}.const 1807)) + (memory.fill (${memtype}.const 50190) (i32.const 27) (${memtype}.const 487)) + (memory.fill (${memtype}.const 62753) (i32.const 28) (${memtype}.const 530)) + (memory.fill (${memtype}.const 36316) (i32.const 29) (${memtype}.const 943)) + (memory.fill (${memtype}.const 6768) (i32.const 30) (${memtype}.const 381)) + (memory.fill (${memtype}.const 51262) (i32.const 31) (${memtype}.const 3089)) + (memory.fill (${memtype}.const 49729) (i32.const 32) (${memtype}.const 658)) + (memory.fill (${memtype}.const 44540) (i32.const 33) (${memtype}.const 1702)) + (memory.fill (${memtype}.const 33342) (i32.const 34) (${memtype}.const 1092)) + (memory.fill (${memtype}.const 50814) (i32.const 35) (${memtype}.const 1410)) + (memory.fill (${memtype}.const 47594) (i32.const 36) (${memtype}.const 2204)) + (memory.fill (${memtype}.const 54123) (i32.const 37) (${memtype}.const 2394)) + (memory.fill (${memtype}.const 55183) (i32.const 38) (${memtype}.const 250)) + (memory.fill (${memtype}.const 22620) (i32.const 39) (${memtype}.const 2097)) + (memory.fill (${memtype}.const 17132) (i32.const 40) (${memtype}.const 3264)) + (memory.fill (${memtype}.const 54331) (i32.const 41) (${memtype}.const 3299)) + (memory.fill (${memtype}.const 39474) (i32.const 42) (${memtype}.const 2796)) + (memory.fill (${memtype}.const 36156) (i32.const 43) (${memtype}.const 2070)) + (memory.fill (${memtype}.const 35308) (i32.const 44) (${memtype}.const 2763)) + (memory.fill (${memtype}.const 32731) (i32.const 45) (${memtype}.const 312)) + (memory.fill (${memtype}.const 63746) (i32.const 46) (${memtype}.const 192)) + (memory.fill (${memtype}.const 30974) (i32.const 47) (${memtype}.const 596)) + (memory.fill (${memtype}.const 16635) (i32.const 48) (${memtype}.const 501)) + (memory.fill (${memtype}.const 57002) (i32.const 49) (${memtype}.const 686)) + (memory.fill (${memtype}.const 34299) (i32.const 50) (${memtype}.const 385)) + (memory.fill (${memtype}.const 60881) (i32.const 51) (${memtype}.const 903)) + (memory.fill (${memtype}.const 61445) (i32.const 52) (${memtype}.const 2390)) + (memory.fill (${memtype}.const 46972) (i32.const 53) (${memtype}.const 1441)) + (memory.fill (${memtype}.const 25973) (i32.const 54) (${memtype}.const 3162)) + (memory.fill (${memtype}.const 5566) (i32.const 55) (${memtype}.const 2135)) + (memory.fill (${memtype}.const 35977) (i32.const 56) (${memtype}.const 519)) + (memory.fill (${memtype}.const 44892) (i32.const 57) (${memtype}.const 3280)) + (memory.fill (${memtype}.const 46760) (i32.const 58) (${memtype}.const 1678)) + (memory.fill (${memtype}.const 46607) (i32.const 59) (${memtype}.const 3168)) + (memory.fill (${memtype}.const 22449) (i32.const 60) (${memtype}.const 1441)) + (memory.fill (${memtype}.const 58609) (i32.const 61) (${memtype}.const 663)) + (memory.fill (${memtype}.const 32261) (i32.const 62) (${memtype}.const 1671)) + (memory.fill (${memtype}.const 3063) (i32.const 63) (${memtype}.const 721)) + (memory.fill (${memtype}.const 34025) (i32.const 64) (${memtype}.const 84)) + (memory.fill (${memtype}.const 33338) (i32.const 65) (${memtype}.const 2029)) + (memory.fill (${memtype}.const 36810) (i32.const 66) (${memtype}.const 29)) + (memory.fill (${memtype}.const 19147) (i32.const 67) (${memtype}.const 3034)) + (memory.fill (${memtype}.const 12616) (i32.const 68) (${memtype}.const 1043)) + (memory.fill (${memtype}.const 18276) (i32.const 69) (${memtype}.const 3324)) + (memory.fill (${memtype}.const 4639) (i32.const 70) (${memtype}.const 1091)) + (memory.fill (${memtype}.const 16158) (i32.const 71) (${memtype}.const 1997)) + (memory.fill (${memtype}.const 18204) (i32.const 72) (${memtype}.const 2259)) + (memory.fill (${memtype}.const 50532) (i32.const 73) (${memtype}.const 3189)) + (memory.fill (${memtype}.const 11028) (i32.const 74) (${memtype}.const 1968)) + (memory.fill (${memtype}.const 15962) (i32.const 75) (${memtype}.const 1455)) + (memory.fill (${memtype}.const 45406) (i32.const 76) (${memtype}.const 1177)) + (memory.fill (${memtype}.const 54137) (i32.const 77) (${memtype}.const 1568)) + (memory.fill (${memtype}.const 33083) (i32.const 78) (${memtype}.const 1642)) + (memory.fill (${memtype}.const 61028) (i32.const 79) (${memtype}.const 3284)) + (memory.fill (${memtype}.const 51729) (i32.const 80) (${memtype}.const 223)) + (memory.fill (${memtype}.const 4361) (i32.const 81) (${memtype}.const 2171)) + (memory.fill (${memtype}.const 57514) (i32.const 82) (${memtype}.const 1322)) + (memory.fill (${memtype}.const 55724) (i32.const 83) (${memtype}.const 2648)) + (memory.fill (${memtype}.const 24091) (i32.const 84) (${memtype}.const 1045)) + (memory.fill (${memtype}.const 43183) (i32.const 85) (${memtype}.const 3097)) + (memory.fill (${memtype}.const 32307) (i32.const 86) (${memtype}.const 2796)) + (memory.fill (${memtype}.const 3811) (i32.const 87) (${memtype}.const 2010)) + (memory.fill (${memtype}.const 54856) (i32.const 88) (${memtype}.const 0)) + (memory.fill (${memtype}.const 49941) (i32.const 89) (${memtype}.const 2069)) + (memory.fill (${memtype}.const 20411) (i32.const 90) (${memtype}.const 2896)) + (memory.fill (${memtype}.const 33826) (i32.const 91) (${memtype}.const 192)) + (memory.fill (${memtype}.const 9402) (i32.const 92) (${memtype}.const 2195)) + (memory.fill (${memtype}.const 12413) (i32.const 93) (${memtype}.const 24)) + (memory.fill (${memtype}.const 14091) (i32.const 94) (${memtype}.const 577)) + (memory.fill (${memtype}.const 44058) (i32.const 95) (${memtype}.const 2089)) + (memory.fill (${memtype}.const 36735) (i32.const 96) (${memtype}.const 3436)) + (memory.fill (${memtype}.const 23288) (i32.const 97) (${memtype}.const 2765)) + (memory.fill (${memtype}.const 6392) (i32.const 98) (${memtype}.const 830)) + (memory.fill (${memtype}.const 33307) (i32.const 99) (${memtype}.const 1938)) + (memory.fill (${memtype}.const 21941) (i32.const 100) (${memtype}.const 2750)) + (memory.copy (${memtype}.const 59214) (${memtype}.const 54248) (${memtype}.const 2098)) + (memory.copy (${memtype}.const 63026) (${memtype}.const 39224) (${memtype}.const 230)) + (memory.copy (${memtype}.const 51833) (${memtype}.const 23629) (${memtype}.const 2300)) + (memory.copy (${memtype}.const 6708) (${memtype}.const 23996) (${memtype}.const 639)) + (memory.copy (${memtype}.const 6990) (${memtype}.const 33399) (${memtype}.const 1097)) + (memory.copy (${memtype}.const 19403) (${memtype}.const 10348) (${memtype}.const 3197)) + (memory.copy (${memtype}.const 27308) (${memtype}.const 54406) (${memtype}.const 100)) + (memory.copy (${memtype}.const 27221) (${memtype}.const 43682) (${memtype}.const 1717)) + (memory.copy (${memtype}.const 60528) (${memtype}.const 8629) (${memtype}.const 119)) + (memory.copy (${memtype}.const 5947) (${memtype}.const 2308) (${memtype}.const 658)) + (memory.copy (${memtype}.const 4787) (${memtype}.const 51631) (${memtype}.const 2269)) + (memory.copy (${memtype}.const 12617) (${memtype}.const 19197) (${memtype}.const 833)) + (memory.copy (${memtype}.const 11854) (${memtype}.const 46505) (${memtype}.const 3300)) + (memory.copy (${memtype}.const 11376) (${memtype}.const 45012) (${memtype}.const 2281)) + (memory.copy (${memtype}.const 34186) (${memtype}.const 6697) (${memtype}.const 2572)) + (memory.copy (${memtype}.const 4936) (${memtype}.const 1690) (${memtype}.const 1328)) + (memory.copy (${memtype}.const 63164) (${memtype}.const 7637) (${memtype}.const 1670)) + (memory.copy (${memtype}.const 44568) (${memtype}.const 18344) (${memtype}.const 33)) + (memory.copy (${memtype}.const 43918) (${memtype}.const 22348) (${memtype}.const 1427)) + (memory.copy (${memtype}.const 46637) (${memtype}.const 49819) (${memtype}.const 1434)) + (memory.copy (${memtype}.const 63684) (${memtype}.const 8755) (${memtype}.const 834)) + (memory.copy (${memtype}.const 33485) (${memtype}.const 20131) (${memtype}.const 3317)) + (memory.copy (${memtype}.const 40575) (${memtype}.const 54317) (${memtype}.const 3201)) + (memory.copy (${memtype}.const 25812) (${memtype}.const 59254) (${memtype}.const 2452)) + (memory.copy (${memtype}.const 19678) (${memtype}.const 56882) (${memtype}.const 346)) + (memory.copy (${memtype}.const 15852) (${memtype}.const 35914) (${memtype}.const 2430)) + (memory.copy (${memtype}.const 11824) (${memtype}.const 35574) (${memtype}.const 300)) + (memory.copy (${memtype}.const 59427) (${memtype}.const 13957) (${memtype}.const 3153)) + (memory.copy (${memtype}.const 34299) (${memtype}.const 60594) (${memtype}.const 1281)) + (memory.copy (${memtype}.const 8964) (${memtype}.const 12276) (${memtype}.const 943)) + (memory.copy (${memtype}.const 2827) (${memtype}.const 10425) (${memtype}.const 1887)) + (memory.copy (${memtype}.const 43194) (${memtype}.const 43910) (${memtype}.const 738)) + (memory.copy (${memtype}.const 63038) (${memtype}.const 18949) (${memtype}.const 122)) + (memory.copy (${memtype}.const 24044) (${memtype}.const 44761) (${memtype}.const 1755)) + (memory.copy (${memtype}.const 22608) (${memtype}.const 14755) (${memtype}.const 702)) + (memory.copy (${memtype}.const 11284) (${memtype}.const 26579) (${memtype}.const 1830)) + (memory.copy (${memtype}.const 23092) (${memtype}.const 20471) (${memtype}.const 1064)) + (memory.copy (${memtype}.const 57248) (${memtype}.const 54770) (${memtype}.const 2631)) + (memory.copy (${memtype}.const 25492) (${memtype}.const 1025) (${memtype}.const 3113)) + (memory.copy (${memtype}.const 49588) (${memtype}.const 44220) (${memtype}.const 975)) + (memory.copy (${memtype}.const 28280) (${memtype}.const 41722) (${memtype}.const 2336)) + (memory.copy (${memtype}.const 61289) (${memtype}.const 230) (${memtype}.const 2872)) + (memory.copy (${memtype}.const 22480) (${memtype}.const 52506) (${memtype}.const 2197)) + (memory.copy (${memtype}.const 40553) (${memtype}.const 9578) (${memtype}.const 1958)) + (memory.copy (${memtype}.const 29004) (${memtype}.const 20862) (${memtype}.const 2186)) + (memory.copy (${memtype}.const 53029) (${memtype}.const 43955) (${memtype}.const 1037)) + (memory.copy (${memtype}.const 25476) (${memtype}.const 35667) (${memtype}.const 1650)) + (memory.copy (${memtype}.const 58516) (${memtype}.const 45819) (${memtype}.const 1986)) + (memory.copy (${memtype}.const 38297) (${memtype}.const 5776) (${memtype}.const 1955)) + (memory.copy (${memtype}.const 28503) (${memtype}.const 55364) (${memtype}.const 2368)) + (memory.copy (${memtype}.const 62619) (${memtype}.const 18108) (${memtype}.const 1356)) + (memory.copy (${memtype}.const 50149) (${memtype}.const 13861) (${memtype}.const 382)) + (memory.copy (${memtype}.const 16904) (${memtype}.const 36341) (${memtype}.const 1900)) + (memory.copy (${memtype}.const 48098) (${memtype}.const 11358) (${memtype}.const 2807)) + (memory.copy (${memtype}.const 28512) (${memtype}.const 40362) (${memtype}.const 323)) + (memory.copy (${memtype}.const 35506) (${memtype}.const 27856) (${memtype}.const 1670)) + (memory.copy (${memtype}.const 62970) (${memtype}.const 53332) (${memtype}.const 1341)) + (memory.copy (${memtype}.const 14133) (${memtype}.const 46312) (${memtype}.const 644)) + (memory.copy (${memtype}.const 29030) (${memtype}.const 19074) (${memtype}.const 496)) + (memory.copy (${memtype}.const 44952) (${memtype}.const 47577) (${memtype}.const 2784)) + (memory.copy (${memtype}.const 39559) (${memtype}.const 44661) (${memtype}.const 1350)) + (memory.copy (${memtype}.const 10352) (${memtype}.const 29274) (${memtype}.const 1475)) + (memory.copy (${memtype}.const 46911) (${memtype}.const 46178) (${memtype}.const 1467)) + (memory.copy (${memtype}.const 4905) (${memtype}.const 28740) (${memtype}.const 1895)) + (memory.copy (${memtype}.const 38012) (${memtype}.const 57253) (${memtype}.const 1751)) + (memory.copy (${memtype}.const 26446) (${memtype}.const 27223) (${memtype}.const 1127)) + (memory.copy (${memtype}.const 58835) (${memtype}.const 24657) (${memtype}.const 1063)) + (memory.copy (${memtype}.const 61356) (${memtype}.const 38790) (${memtype}.const 766)) + (memory.copy (${memtype}.const 44160) (${memtype}.const 2284) (${memtype}.const 1520)) + (memory.copy (${memtype}.const 32740) (${memtype}.const 47237) (${memtype}.const 3014)) + (memory.copy (${memtype}.const 11148) (${memtype}.const 21260) (${memtype}.const 1011)) + (memory.copy (${memtype}.const 7665) (${memtype}.const 31612) (${memtype}.const 3034)) + (memory.copy (${memtype}.const 18044) (${memtype}.const 12987) (${memtype}.const 3320)) + (memory.copy (${memtype}.const 57306) (${memtype}.const 55905) (${memtype}.const 308)) + (memory.copy (${memtype}.const 24675) (${memtype}.const 16815) (${memtype}.const 1155)) + (memory.copy (${memtype}.const 19900) (${memtype}.const 10115) (${memtype}.const 722)) + (memory.copy (${memtype}.const 2921) (${memtype}.const 5935) (${memtype}.const 2370)) + (memory.copy (${memtype}.const 32255) (${memtype}.const 50095) (${memtype}.const 2926)) + (memory.copy (${memtype}.const 15126) (${memtype}.const 17299) (${memtype}.const 2607)) + (memory.copy (${memtype}.const 45575) (${memtype}.const 28447) (${memtype}.const 2045)) + (memory.copy (${memtype}.const 55149) (${memtype}.const 36113) (${memtype}.const 2596)) + (memory.copy (${memtype}.const 28461) (${memtype}.const 54157) (${memtype}.const 1168)) + (memory.copy (${memtype}.const 47951) (${memtype}.const 53385) (${memtype}.const 3137)) + (memory.copy (${memtype}.const 30646) (${memtype}.const 45155) (${memtype}.const 2649)) + (memory.copy (${memtype}.const 5057) (${memtype}.const 4295) (${memtype}.const 52)) + (memory.copy (${memtype}.const 6692) (${memtype}.const 24195) (${memtype}.const 441)) + (memory.copy (${memtype}.const 32984) (${memtype}.const 27117) (${memtype}.const 3445)) + (memory.copy (${memtype}.const 32530) (${memtype}.const 59372) (${memtype}.const 2785)) + (memory.copy (${memtype}.const 34361) (${memtype}.const 8962) (${memtype}.const 2406)) + (memory.copy (${memtype}.const 17893) (${memtype}.const 54538) (${memtype}.const 3381)) + (memory.copy (${memtype}.const 22685) (${memtype}.const 44151) (${memtype}.const 136)) + (memory.copy (${memtype}.const 59089) (${memtype}.const 7077) (${memtype}.const 1045)) + (memory.copy (${memtype}.const 42945) (${memtype}.const 55028) (${memtype}.const 2389)) + (memory.copy (${memtype}.const 44693) (${memtype}.const 20138) (${memtype}.const 877)) + (memory.copy (${memtype}.const 36810) (${memtype}.const 25196) (${memtype}.const 3447)) + (memory.copy (${memtype}.const 45742) (${memtype}.const 31888) (${memtype}.const 854)) + (memory.copy (${memtype}.const 24236) (${memtype}.const 31866) (${memtype}.const 1377)) + (memory.copy (${memtype}.const 33778) (${memtype}.const 692) (${memtype}.const 1594)) + (memory.copy (${memtype}.const 60618) (${memtype}.const 18585) (${memtype}.const 2987)) + (memory.copy (${memtype}.const 50370) (${memtype}.const 41271) (${memtype}.const 1406)) ) - ${checkRangeCode()}) + ${checkRangeCode(memtype)}) (invoke "test") `); -checkRange(0, 124, 0); -checkRange(124, 1517, 9); -checkRange(1517, 2132, 0); -checkRange(2132, 2827, 10); -checkRange(2827, 2921, 92); -checkRange(2921, 3538, 83); -checkRange(3538, 3786, 77); -checkRange(3786, 4042, 97); -checkRange(4042, 4651, 99); -checkRange(4651, 5057, 0); -checkRange(5057, 5109, 99); -checkRange(5109, 5291, 0); -checkRange(5291, 5524, 72); -checkRange(5524, 5691, 92); -checkRange(5691, 6552, 83); -checkRange(6552, 7133, 77); -checkRange(7133, 7665, 99); -checkRange(7665, 8314, 0); -checkRange(8314, 8360, 62); -checkRange(8360, 8793, 86); -checkRange(8793, 8979, 83); -checkRange(8979, 9373, 79); -checkRange(9373, 9518, 95); -checkRange(9518, 9934, 59); -checkRange(9934, 10087, 77); -checkRange(10087, 10206, 5); -checkRange(10206, 10230, 77); -checkRange(10230, 10249, 41); -checkRange(10249, 11148, 83); -checkRange(11148, 11356, 74); -checkRange(11356, 11380, 93); -checkRange(11380, 11939, 74); -checkRange(11939, 12159, 68); -checkRange(12159, 12575, 83); -checkRange(12575, 12969, 79); -checkRange(12969, 13114, 95); -checkRange(13114, 14133, 59); -checkRange(14133, 14404, 76); -checkRange(14404, 14428, 57); -checkRange(14428, 14458, 59); -checkRange(14458, 14580, 32); -checkRange(14580, 14777, 89); -checkRange(14777, 15124, 59); -checkRange(15124, 15126, 36); -checkRange(15126, 15192, 100); -checkRange(15192, 15871, 96); -checkRange(15871, 15998, 95); -checkRange(15998, 17017, 59); -checkRange(17017, 17288, 76); -checkRange(17288, 17312, 57); -checkRange(17312, 17342, 59); -checkRange(17342, 17464, 32); -checkRange(17464, 17661, 89); -checkRange(17661, 17727, 59); -checkRange(17727, 17733, 5); -checkRange(17733, 17893, 96); -checkRange(17893, 18553, 77); -checkRange(18553, 18744, 42); -checkRange(18744, 18801, 76); -checkRange(18801, 18825, 57); -checkRange(18825, 18876, 59); -checkRange(18876, 18885, 77); -checkRange(18885, 18904, 41); -checkRange(18904, 19567, 83); -checkRange(19567, 20403, 96); -checkRange(20403, 21274, 77); -checkRange(21274, 21364, 100); -checkRange(21364, 21468, 74); -checkRange(21468, 21492, 93); -checkRange(21492, 22051, 74); -checkRange(22051, 22480, 68); -checkRange(22480, 22685, 100); -checkRange(22685, 22694, 68); -checkRange(22694, 22821, 10); -checkRange(22821, 22869, 100); -checkRange(22869, 24107, 97); -checkRange(24107, 24111, 37); -checkRange(24111, 24236, 77); -checkRange(24236, 24348, 72); -checkRange(24348, 24515, 92); -checkRange(24515, 24900, 83); -checkRange(24900, 25136, 95); -checkRange(25136, 25182, 85); -checkRange(25182, 25426, 68); -checkRange(25426, 25613, 89); -checkRange(25613, 25830, 96); -checkRange(25830, 26446, 100); -checkRange(26446, 26517, 10); -checkRange(26517, 27468, 92); -checkRange(27468, 27503, 95); -checkRange(27503, 27573, 77); -checkRange(27573, 28245, 92); -checkRange(28245, 28280, 95); -checkRange(28280, 29502, 77); -checkRange(29502, 29629, 42); -checkRange(29629, 30387, 83); -checkRange(30387, 30646, 77); -checkRange(30646, 31066, 92); -checkRange(31066, 31131, 77); -checkRange(31131, 31322, 42); -checkRange(31322, 31379, 76); -checkRange(31379, 31403, 57); -checkRange(31403, 31454, 59); -checkRange(31454, 31463, 77); -checkRange(31463, 31482, 41); -checkRange(31482, 31649, 83); -checkRange(31649, 31978, 72); -checkRange(31978, 32145, 92); -checkRange(32145, 32530, 83); -checkRange(32530, 32766, 95); -checkRange(32766, 32812, 85); -checkRange(32812, 33056, 68); -checkRange(33056, 33660, 89); -checkRange(33660, 33752, 59); -checkRange(33752, 33775, 36); -checkRange(33775, 33778, 32); -checkRange(33778, 34603, 9); -checkRange(34603, 35218, 0); -checkRange(35218, 35372, 10); -checkRange(35372, 35486, 77); -checkRange(35486, 35605, 5); -checkRange(35605, 35629, 77); -checkRange(35629, 35648, 41); -checkRange(35648, 36547, 83); -checkRange(36547, 36755, 74); -checkRange(36755, 36767, 93); -checkRange(36767, 36810, 83); -checkRange(36810, 36839, 100); -checkRange(36839, 37444, 96); -checkRange(37444, 38060, 100); -checkRange(38060, 38131, 10); -checkRange(38131, 39082, 92); -checkRange(39082, 39117, 95); -checkRange(39117, 39187, 77); -checkRange(39187, 39859, 92); -checkRange(39859, 39894, 95); -checkRange(39894, 40257, 77); -checkRange(40257, 40344, 89); -checkRange(40344, 40371, 59); -checkRange(40371, 40804, 77); -checkRange(40804, 40909, 5); -checkRange(40909, 42259, 92); -checkRange(42259, 42511, 77); -checkRange(42511, 42945, 83); -checkRange(42945, 43115, 77); -checkRange(43115, 43306, 42); -checkRange(43306, 43363, 76); -checkRange(43363, 43387, 57); -checkRange(43387, 43438, 59); -checkRange(43438, 43447, 77); -checkRange(43447, 43466, 41); -checkRange(43466, 44129, 83); -checkRange(44129, 44958, 96); -checkRange(44958, 45570, 77); -checkRange(45570, 45575, 92); -checkRange(45575, 45640, 77); -checkRange(45640, 45742, 42); -checkRange(45742, 45832, 72); -checkRange(45832, 45999, 92); -checkRange(45999, 46384, 83); -checkRange(46384, 46596, 95); -checkRange(46596, 46654, 92); -checkRange(46654, 47515, 83); -checkRange(47515, 47620, 77); -checkRange(47620, 47817, 79); -checkRange(47817, 47951, 95); -checkRange(47951, 48632, 100); -checkRange(48632, 48699, 97); -checkRange(48699, 48703, 37); -checkRange(48703, 49764, 77); -checkRange(49764, 49955, 42); -checkRange(49955, 50012, 76); -checkRange(50012, 50036, 57); -checkRange(50036, 50087, 59); -checkRange(50087, 50096, 77); -checkRange(50096, 50115, 41); -checkRange(50115, 50370, 83); -checkRange(50370, 51358, 92); -checkRange(51358, 51610, 77); -checkRange(51610, 51776, 83); -checkRange(51776, 51833, 89); -checkRange(51833, 52895, 100); -checkRange(52895, 53029, 97); -checkRange(53029, 53244, 68); -checkRange(53244, 54066, 100); -checkRange(54066, 54133, 97); -checkRange(54133, 54137, 37); -checkRange(54137, 55198, 77); -checkRange(55198, 55389, 42); -checkRange(55389, 55446, 76); -checkRange(55446, 55470, 57); -checkRange(55470, 55521, 59); -checkRange(55521, 55530, 77); -checkRange(55530, 55549, 41); -checkRange(55549, 56212, 83); -checkRange(56212, 57048, 96); -checkRange(57048, 58183, 77); -checkRange(58183, 58202, 41); -checkRange(58202, 58516, 83); -checkRange(58516, 58835, 95); -checkRange(58835, 58855, 77); -checkRange(58855, 59089, 95); -checkRange(59089, 59145, 77); -checkRange(59145, 59677, 99); -checkRange(59677, 60134, 0); -checkRange(60134, 60502, 89); -checkRange(60502, 60594, 59); -checkRange(60594, 60617, 36); -checkRange(60617, 60618, 32); -checkRange(60618, 60777, 42); -checkRange(60777, 60834, 76); -checkRange(60834, 60858, 57); -checkRange(60858, 60909, 59); -checkRange(60909, 60918, 77); -checkRange(60918, 60937, 41); -checkRange(60937, 61600, 83); -checkRange(61600, 62436, 96); -checkRange(62436, 63307, 77); -checkRange(63307, 63397, 100); -checkRange(63397, 63501, 74); -checkRange(63501, 63525, 93); -checkRange(63525, 63605, 74); -checkRange(63605, 63704, 100); -checkRange(63704, 63771, 97); -checkRange(63771, 63775, 37); -checkRange(63775, 64311, 77); -checkRange(64311, 64331, 26); -checkRange(64331, 64518, 92); -checkRange(64518, 64827, 11); -checkRange(64827, 64834, 26); -checkRange(64834, 65536, 0); + checkRange(memtype, 0, 124, 0); + checkRange(memtype, 124, 1517, 9); + checkRange(memtype, 1517, 2132, 0); + checkRange(memtype, 2132, 2827, 10); + checkRange(memtype, 2827, 2921, 92); + checkRange(memtype, 2921, 3538, 83); + checkRange(memtype, 3538, 3786, 77); + checkRange(memtype, 3786, 4042, 97); + checkRange(memtype, 4042, 4651, 99); + checkRange(memtype, 4651, 5057, 0); + checkRange(memtype, 5057, 5109, 99); + checkRange(memtype, 5109, 5291, 0); + checkRange(memtype, 5291, 5524, 72); + checkRange(memtype, 5524, 5691, 92); + checkRange(memtype, 5691, 6552, 83); + checkRange(memtype, 6552, 7133, 77); + checkRange(memtype, 7133, 7665, 99); + checkRange(memtype, 7665, 8314, 0); + checkRange(memtype, 8314, 8360, 62); + checkRange(memtype, 8360, 8793, 86); + checkRange(memtype, 8793, 8979, 83); + checkRange(memtype, 8979, 9373, 79); + checkRange(memtype, 9373, 9518, 95); + checkRange(memtype, 9518, 9934, 59); + checkRange(memtype, 9934, 10087, 77); + checkRange(memtype, 10087, 10206, 5); + checkRange(memtype, 10206, 10230, 77); + checkRange(memtype, 10230, 10249, 41); + checkRange(memtype, 10249, 11148, 83); + checkRange(memtype, 11148, 11356, 74); + checkRange(memtype, 11356, 11380, 93); + checkRange(memtype, 11380, 11939, 74); + checkRange(memtype, 11939, 12159, 68); + checkRange(memtype, 12159, 12575, 83); + checkRange(memtype, 12575, 12969, 79); + checkRange(memtype, 12969, 13114, 95); + checkRange(memtype, 13114, 14133, 59); + checkRange(memtype, 14133, 14404, 76); + checkRange(memtype, 14404, 14428, 57); + checkRange(memtype, 14428, 14458, 59); + checkRange(memtype, 14458, 14580, 32); + checkRange(memtype, 14580, 14777, 89); + checkRange(memtype, 14777, 15124, 59); + checkRange(memtype, 15124, 15126, 36); + checkRange(memtype, 15126, 15192, 100); + checkRange(memtype, 15192, 15871, 96); + checkRange(memtype, 15871, 15998, 95); + checkRange(memtype, 15998, 17017, 59); + checkRange(memtype, 17017, 17288, 76); + checkRange(memtype, 17288, 17312, 57); + checkRange(memtype, 17312, 17342, 59); + checkRange(memtype, 17342, 17464, 32); + checkRange(memtype, 17464, 17661, 89); + checkRange(memtype, 17661, 17727, 59); + checkRange(memtype, 17727, 17733, 5); + checkRange(memtype, 17733, 17893, 96); + checkRange(memtype, 17893, 18553, 77); + checkRange(memtype, 18553, 18744, 42); + checkRange(memtype, 18744, 18801, 76); + checkRange(memtype, 18801, 18825, 57); + checkRange(memtype, 18825, 18876, 59); + checkRange(memtype, 18876, 18885, 77); + checkRange(memtype, 18885, 18904, 41); + checkRange(memtype, 18904, 19567, 83); + checkRange(memtype, 19567, 20403, 96); + checkRange(memtype, 20403, 21274, 77); + checkRange(memtype, 21274, 21364, 100); + checkRange(memtype, 21364, 21468, 74); + checkRange(memtype, 21468, 21492, 93); + checkRange(memtype, 21492, 22051, 74); + checkRange(memtype, 22051, 22480, 68); + checkRange(memtype, 22480, 22685, 100); + checkRange(memtype, 22685, 22694, 68); + checkRange(memtype, 22694, 22821, 10); + checkRange(memtype, 22821, 22869, 100); + checkRange(memtype, 22869, 24107, 97); + checkRange(memtype, 24107, 24111, 37); + checkRange(memtype, 24111, 24236, 77); + checkRange(memtype, 24236, 24348, 72); + checkRange(memtype, 24348, 24515, 92); + checkRange(memtype, 24515, 24900, 83); + checkRange(memtype, 24900, 25136, 95); + checkRange(memtype, 25136, 25182, 85); + checkRange(memtype, 25182, 25426, 68); + checkRange(memtype, 25426, 25613, 89); + checkRange(memtype, 25613, 25830, 96); + checkRange(memtype, 25830, 26446, 100); + checkRange(memtype, 26446, 26517, 10); + checkRange(memtype, 26517, 27468, 92); + checkRange(memtype, 27468, 27503, 95); + checkRange(memtype, 27503, 27573, 77); + checkRange(memtype, 27573, 28245, 92); + checkRange(memtype, 28245, 28280, 95); + checkRange(memtype, 28280, 29502, 77); + checkRange(memtype, 29502, 29629, 42); + checkRange(memtype, 29629, 30387, 83); + checkRange(memtype, 30387, 30646, 77); + checkRange(memtype, 30646, 31066, 92); + checkRange(memtype, 31066, 31131, 77); + checkRange(memtype, 31131, 31322, 42); + checkRange(memtype, 31322, 31379, 76); + checkRange(memtype, 31379, 31403, 57); + checkRange(memtype, 31403, 31454, 59); + checkRange(memtype, 31454, 31463, 77); + checkRange(memtype, 31463, 31482, 41); + checkRange(memtype, 31482, 31649, 83); + checkRange(memtype, 31649, 31978, 72); + checkRange(memtype, 31978, 32145, 92); + checkRange(memtype, 32145, 32530, 83); + checkRange(memtype, 32530, 32766, 95); + checkRange(memtype, 32766, 32812, 85); + checkRange(memtype, 32812, 33056, 68); + checkRange(memtype, 33056, 33660, 89); + checkRange(memtype, 33660, 33752, 59); + checkRange(memtype, 33752, 33775, 36); + checkRange(memtype, 33775, 33778, 32); + checkRange(memtype, 33778, 34603, 9); + checkRange(memtype, 34603, 35218, 0); + checkRange(memtype, 35218, 35372, 10); + checkRange(memtype, 35372, 35486, 77); + checkRange(memtype, 35486, 35605, 5); + checkRange(memtype, 35605, 35629, 77); + checkRange(memtype, 35629, 35648, 41); + checkRange(memtype, 35648, 36547, 83); + checkRange(memtype, 36547, 36755, 74); + checkRange(memtype, 36755, 36767, 93); + checkRange(memtype, 36767, 36810, 83); + checkRange(memtype, 36810, 36839, 100); + checkRange(memtype, 36839, 37444, 96); + checkRange(memtype, 37444, 38060, 100); + checkRange(memtype, 38060, 38131, 10); + checkRange(memtype, 38131, 39082, 92); + checkRange(memtype, 39082, 39117, 95); + checkRange(memtype, 39117, 39187, 77); + checkRange(memtype, 39187, 39859, 92); + checkRange(memtype, 39859, 39894, 95); + checkRange(memtype, 39894, 40257, 77); + checkRange(memtype, 40257, 40344, 89); + checkRange(memtype, 40344, 40371, 59); + checkRange(memtype, 40371, 40804, 77); + checkRange(memtype, 40804, 40909, 5); + checkRange(memtype, 40909, 42259, 92); + checkRange(memtype, 42259, 42511, 77); + checkRange(memtype, 42511, 42945, 83); + checkRange(memtype, 42945, 43115, 77); + checkRange(memtype, 43115, 43306, 42); + checkRange(memtype, 43306, 43363, 76); + checkRange(memtype, 43363, 43387, 57); + checkRange(memtype, 43387, 43438, 59); + checkRange(memtype, 43438, 43447, 77); + checkRange(memtype, 43447, 43466, 41); + checkRange(memtype, 43466, 44129, 83); + checkRange(memtype, 44129, 44958, 96); + checkRange(memtype, 44958, 45570, 77); + checkRange(memtype, 45570, 45575, 92); + checkRange(memtype, 45575, 45640, 77); + checkRange(memtype, 45640, 45742, 42); + checkRange(memtype, 45742, 45832, 72); + checkRange(memtype, 45832, 45999, 92); + checkRange(memtype, 45999, 46384, 83); + checkRange(memtype, 46384, 46596, 95); + checkRange(memtype, 46596, 46654, 92); + checkRange(memtype, 46654, 47515, 83); + checkRange(memtype, 47515, 47620, 77); + checkRange(memtype, 47620, 47817, 79); + checkRange(memtype, 47817, 47951, 95); + checkRange(memtype, 47951, 48632, 100); + checkRange(memtype, 48632, 48699, 97); + checkRange(memtype, 48699, 48703, 37); + checkRange(memtype, 48703, 49764, 77); + checkRange(memtype, 49764, 49955, 42); + checkRange(memtype, 49955, 50012, 76); + checkRange(memtype, 50012, 50036, 57); + checkRange(memtype, 50036, 50087, 59); + checkRange(memtype, 50087, 50096, 77); + checkRange(memtype, 50096, 50115, 41); + checkRange(memtype, 50115, 50370, 83); + checkRange(memtype, 50370, 51358, 92); + checkRange(memtype, 51358, 51610, 77); + checkRange(memtype, 51610, 51776, 83); + checkRange(memtype, 51776, 51833, 89); + checkRange(memtype, 51833, 52895, 100); + checkRange(memtype, 52895, 53029, 97); + checkRange(memtype, 53029, 53244, 68); + checkRange(memtype, 53244, 54066, 100); + checkRange(memtype, 54066, 54133, 97); + checkRange(memtype, 54133, 54137, 37); + checkRange(memtype, 54137, 55198, 77); + checkRange(memtype, 55198, 55389, 42); + checkRange(memtype, 55389, 55446, 76); + checkRange(memtype, 55446, 55470, 57); + checkRange(memtype, 55470, 55521, 59); + checkRange(memtype, 55521, 55530, 77); + checkRange(memtype, 55530, 55549, 41); + checkRange(memtype, 55549, 56212, 83); + checkRange(memtype, 56212, 57048, 96); + checkRange(memtype, 57048, 58183, 77); + checkRange(memtype, 58183, 58202, 41); + checkRange(memtype, 58202, 58516, 83); + checkRange(memtype, 58516, 58835, 95); + checkRange(memtype, 58835, 58855, 77); + checkRange(memtype, 58855, 59089, 95); + checkRange(memtype, 59089, 59145, 77); + checkRange(memtype, 59145, 59677, 99); + checkRange(memtype, 59677, 60134, 0); + checkRange(memtype, 60134, 60502, 89); + checkRange(memtype, 60502, 60594, 59); + checkRange(memtype, 60594, 60617, 36); + checkRange(memtype, 60617, 60618, 32); + checkRange(memtype, 60618, 60777, 42); + checkRange(memtype, 60777, 60834, 76); + checkRange(memtype, 60834, 60858, 57); + checkRange(memtype, 60858, 60909, 59); + checkRange(memtype, 60909, 60918, 77); + checkRange(memtype, 60918, 60937, 41); + checkRange(memtype, 60937, 61600, 83); + checkRange(memtype, 61600, 62436, 96); + checkRange(memtype, 62436, 63307, 77); + checkRange(memtype, 63307, 63397, 100); + checkRange(memtype, 63397, 63501, 74); + checkRange(memtype, 63501, 63525, 93); + checkRange(memtype, 63525, 63605, 74); + checkRange(memtype, 63605, 63704, 100); + checkRange(memtype, 63704, 63771, 97); + checkRange(memtype, 63771, 63775, 37); + checkRange(memtype, 63775, 64311, 77); + checkRange(memtype, 64311, 64331, 26); + checkRange(memtype, 64331, 64518, 92); + checkRange(memtype, 64518, 64827, 11); + checkRange(memtype, 64827, 64834, 26); + checkRange(memtype, 64834, 65536, 0); +} diff --git a/test/meta/generate_memory_fill.js b/test/meta/generate_memory_fill.js index 3a8f485f1..b04c3738a 100644 --- a/test/meta/generate_memory_fill.js +++ b/test/meta/generate_memory_fill.js @@ -3,155 +3,160 @@ print_origin("generate_memory_fill.js"); -let PREAMBLE = - `(memory 1 1) - ${checkRangeCode()}`; +for ( const memtype of ['i32', 'i64'] ) { -// Range valid -print( + const decltype = memtype == 'i64' ? ' i64' : ''; + + let PREAMBLE = + `(memory${decltype} 1 1) + ${checkRangeCode(memtype)}`; + + // Range valid + print( ` (module ${PREAMBLE} (func (export "test") - (memory.fill (i32.const 0xFF00) (i32.const 0x55) (i32.const 256)))) + (memory.fill (${memtype}.const 0xFF00) (i32.const 0x55) (${memtype}.const 256)))) (invoke "test") `); -checkRange(0x00000, 0x0FF00, 0x00) -checkRange(0x0FF00, 0x10000, 0x55) + checkRange(memtype, 0x00000, 0x0FF00, 0x00) + checkRange(memtype, 0x0FF00, 0x10000, 0x55) -// Range invalid -print( + // Range invalid + print( `(module ${PREAMBLE} (func (export "test") - (memory.fill (i32.const 0xFF00) (i32.const 0x55) (i32.const 257)))) + (memory.fill (${memtype}.const 0xFF00) (i32.const 0x55) (${memtype}.const 257)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// Wraparound the end of 32-bit offset space -print( + // Wraparound the end of 32-bit offset space + print( `(module ${PREAMBLE} (func (export "test") - (memory.fill (i32.const 0xFFFFFF00) (i32.const 0x55) (i32.const 257)))) + (memory.fill (${memtype}.const 0xFFFFFF00) (i32.const 0x55) (${memtype}.const 257)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// Zero len with offset in-bounds is a no-op -print( + // Zero len with offset in-bounds is a no-op + print( `(module ${PREAMBLE} (func (export "test") - (memory.fill (i32.const 0x12) (i32.const 0x55) (i32.const 0)))) + (memory.fill (${memtype}.const 0x12) (i32.const 0x55) (${memtype}.const 0)))) (invoke "test") `); -checkRange(0x00000, 0x10000, 0x00); + checkRange(memtype, 0x00000, 0x10000, 0x00); -// Zero len with offset out-of-bounds at the end of memory is allowed -print( + // Zero len with offset out-of-bounds at the end of memory is allowed + print( `(module ${PREAMBLE} (func (export "test") - (memory.fill (i32.const 0x10000) (i32.const 0x55) (i32.const 0)))) + (memory.fill (${memtype}.const 0x10000) (i32.const 0x55) (${memtype}.const 0)))) (invoke "test") `); -// Zero len with offset out-of-bounds past the end of memory is not allowed -print( + // Zero len with offset out-of-bounds past the end of memory is not allowed + print( `(module ${PREAMBLE} (func (export "test") - (memory.fill (i32.const 0x20000) (i32.const 0x55) (i32.const 0)))) + (memory.fill (${memtype}.const 0x20000) (i32.const 0x55) (${memtype}.const 0)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// Very large range -print( + // Very large range + print( `(module ${PREAMBLE} (func (export "test") - (memory.fill (i32.const 0x1) (i32.const 0xAA) (i32.const 0xFFFE)))) + (memory.fill (${memtype}.const 0x1) (i32.const 0xAA) (${memtype}.const 0xFFFE)))) (invoke "test") `); -checkRange(0x00000, 0x00001, 0x00); -checkRange(0x00001, 0x0FFFF, 0xAA); -checkRange(0x0FFFF, 0x10000, 0x00); + checkRange(memtype, 0x00000, 0x00001, 0x00); + checkRange(memtype, 0x00001, 0x0FFFF, 0xAA); + checkRange(memtype, 0x0FFFF, 0x10000, 0x00); -// Sequencing -print( + // Sequencing + print( ` (module ${PREAMBLE} (func (export "test") - (memory.fill (i32.const 0x12) (i32.const 0x55) (i32.const 10)) - (memory.fill (i32.const 0x15) (i32.const 0xAA) (i32.const 4)))) + (memory.fill (${memtype}.const 0x12) (i32.const 0x55) (${memtype}.const 10)) + (memory.fill (${memtype}.const 0x15) (i32.const 0xAA) (${memtype}.const 4)))) (invoke "test") `); -checkRange(0x0, 0x12+0, 0x00); -checkRange(0x12+0, 0x12+3, 0x55); -checkRange(0x12+3, 0x12+7, 0xAA); -checkRange(0x12+7, 0x12+10, 0x55); -checkRange(0x12+10, 0x10000, 0x00); + checkRange(memtype, 0x0, 0x12+0, 0x00); + checkRange(memtype, 0x12+0, 0x12+3, 0x55); + checkRange(memtype, 0x12+3, 0x12+7, 0xAA); + checkRange(memtype, 0x12+7, 0x12+10, 0x55); + checkRange(memtype, 0x12+10, 0x10000, 0x00); -// Sundry compilation failures. + // Sundry compilation failures. -// Module doesn't have a memory. -print( + // Module doesn't have a memory. + print( `(assert_invalid (module (func (export "testfn") - (memory.fill (i32.const 10) (i32.const 20) (i32.const 30)))) + (memory.fill (${memtype}.const 10) (i32.const 20) (${memtype}.const 30)))) "unknown memory 0") `); -// Invalid argument types. TODO: We can add anyref, funcref, etc here. -{ - const tys = ['i32', 'f32', 'i64', 'f64']; - for (let ty1 of tys) { - for (let ty2 of tys) { - for (let ty3 of tys) { - if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32') - continue; // this is the only valid case - print( + // Invalid argument types. TODO: We can add anyref, funcref, etc here. + { + const tys = ['i32', 'f32', 'i64', 'f64']; + for (let ty1 of tys) { + for (let ty2 of tys) { + for (let ty3 of tys) { + if (ty1 == memtype && ty2 == 'i32' && ty3 == memtype) + continue; // this is the only valid case + print( `(assert_invalid (module - (memory 1 1) + (memory${decltype} 1 1) (func (export "testfn") (memory.fill (${ty1}.const 10) (${ty2}.const 20) (${ty3}.const 30)))) "type mismatch") `); - }}} -} + }}} + } -// memory.fill: out of bounds, and should not perform a partial fill. -// -// Arithmetic overflow of memory offset + len should not affect the behavior, we -// should still fill up to the limit. + // memory.fill: out of bounds, and should not perform a partial fill. + // + // Arithmetic overflow of memory offset + len should not affect the behavior, we + // should still fill up to the limit. -function mem_fill(min, max, shared, backup, write=backup*2) { - print( + function mem_fill(min, max, shared, backup, write=backup*2) { + print( `(module - (memory ${min} ${max} ${shared}) - ${checkRangeCode()} - (func (export "run") (param $offs i32) (param $val i32) (param $len i32) + (memory${decltype} ${min} ${max} ${shared}) + ${checkRangeCode(memtype)} + (func (export "run") (param $offs ${memtype}) (param $val i32) (param $len ${memtype}) (memory.fill (local.get $offs) (local.get $val) (local.get $len)))) `); - // A fill past the end should throw *and* not have performed a partial fill - let offs = min*PAGESIZE - backup; - let val = 37; - print( -`(assert_trap (invoke "run" (i32.const ${offs}) (i32.const ${val}) (i32.const ${write})) + // A fill past the end should throw *and* not have performed a partial fill + let offs = min*PAGESIZE - backup; + let val = 37; + print( +`(assert_trap (invoke "run" (${memtype}.const ${offs}) (i32.const ${val}) (${memtype}.const ${write})) "out of bounds memory access") `); - checkRange(0, min, 0); -} - -mem_fill(1, 1, "", 256); -mem_fill(1, 1, "", 257); -mem_fill(1, 1, "", 257, 0xFFFFFFFF); // offs + len overflows 32-bit - -if (WITH_SHARED_MEMORY) { - mem_fill(2, 4, "shared", 256); - mem_fill(2, 4, "shared", 257); - mem_fill(2, 4, "shared", 257, 0xFFFFFFFF); // offs + len overflows 32-bit + checkRange(memtype, 0, min, 0); + } + + mem_fill(1, 1, "", 256); + mem_fill(1, 1, "", 257); + mem_fill(1, 1, "", 257, 0xFFFFFFFF); // offs + len overflows 32-bit + + if (WITH_SHARED_MEMORY) { + mem_fill(2, 4, "shared", 256); + mem_fill(2, 4, "shared", 257); + mem_fill(2, 4, "shared", 257, 0xFFFFFFFF); // offs + len overflows 32-bit + } } diff --git a/test/meta/generate_memory_init.js b/test/meta/generate_memory_init.js index f8075cc33..fa8ed22b3 100644 --- a/test/meta/generate_memory_init.js +++ b/test/meta/generate_memory_init.js @@ -3,64 +3,67 @@ print_origin("generate_memory_init.js"); -// In-bounds tests. +for ( const memtype of ['i32', 'i64'] ) { -function mem_test(instruction, expected_result_vector) { - print( + const decltype = memtype == 'i64' ? ' i64' : ''; + + // In-bounds tests. + function mem_test(instruction, expected_result_vector) { + print( ` (module - (memory (export "memory0") 1 1) - (data (i32.const 2) "\\03\\01\\04\\01") + (memory (export "memory0")${decltype} 1 1) + (data (${memtype}.const 2) "\\03\\01\\04\\01") (data "\\02\\07\\01\\08") - (data (i32.const 12) "\\07\\05\\02\\03\\06") + (data (${memtype}.const 12) "\\07\\05\\02\\03\\06") (data "\\05\\09\\02\\07\\06") (func (export "test") ${instruction}) - (func (export "load8_u") (param i32) (result i32) + (func (export "load8_u") (param ${memtype}) (result i32) (i32.load8_u (local.get 0)))) (invoke "test") `); - for (let i = 0; i < expected_result_vector.length; i++) { - print(`(assert_return (invoke "load8_u" (i32.const ${i})) (i32.const ${expected_result_vector[i]}))`); - } -} + for (let i = 0; i < expected_result_vector.length; i++) { + print(`(assert_return (invoke "load8_u" (${memtype}.const ${i})) (i32.const ${expected_result_vector[i]}))`); + } + } -const e = 0; + const e = 0; -// This just gives the initial state of the memory, with its active -// initialisers applied. -mem_test("(nop)", - [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); + // This just gives the initial state of the memory, with its active + // initialisers applied. + mem_test("(nop)", + [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); -// Passive init that overwrites all-zero entries -mem_test("(memory.init 1 (i32.const 7) (i32.const 0) (i32.const 4))", - [e,e,3,1,4, 1,e,2,7,1, 8,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); + // Passive init that overwrites all-zero entries + mem_test(`(memory.init 1 (${memtype}.const 7) (i32.const 0) (i32.const 4))`, + [e,e,3,1,4, 1,e,2,7,1, 8,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); -// Passive init that overwrites existing active-init-created entries -mem_test("(memory.init 3 (i32.const 15) (i32.const 1) (i32.const 3))", - [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 9,2,7,e,e, e,e,e,e,e, e,e,e,e,e]); + // Passive init that overwrites existing active-init-created entries + mem_test(`(memory.init 3 (${memtype}.const 15) (i32.const 1) (i32.const 3))`, + [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 9,2,7,e,e, e,e,e,e,e, e,e,e,e,e]); -// Perform active and passive initialisation and then multiple copies -mem_test(`(memory.init 1 (i32.const 7) (i32.const 0) (i32.const 4)) + // Perform active and passive initialisation and then multiple copies + mem_test(`(memory.init 1 (${memtype}.const 7) (i32.const 0) (i32.const 4)) (data.drop 1) - (memory.init 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (memory.init 3 (${memtype}.const 15) (i32.const 1) (i32.const 3)) (data.drop 3) - (memory.copy (i32.const 20) (i32.const 15) (i32.const 5)) - (memory.copy (i32.const 21) (i32.const 29) (i32.const 1)) - (memory.copy (i32.const 24) (i32.const 10) (i32.const 1)) - (memory.copy (i32.const 13) (i32.const 11) (i32.const 4)) - (memory.copy (i32.const 19) (i32.const 20) (i32.const 5))`, - [e,e,3,1,4, 1,e,2,7,1, 8,e,7,e,7, 5,2,7,e,9, e,7,e,8,8, e,e,e,e,e]); + (memory.copy (${memtype}.const 20) (${memtype}.const 15) (${memtype}.const 5)) + (memory.copy (${memtype}.const 21) (${memtype}.const 29) (${memtype}.const 1)) + (memory.copy (${memtype}.const 24) (${memtype}.const 10) (${memtype}.const 1)) + (memory.copy (${memtype}.const 13) (${memtype}.const 11) (${memtype}.const 4)) + (memory.copy (${memtype}.const 19) (${memtype}.const 20) (${memtype}.const 5))`, + [e,e,3,1,4, 1,e,2,7,1, 8,e,7,e,7, 5,2,7,e,9, e,7,e,8,8, e,e,e,e,e]); -// Miscellaneous + // Miscellaneous -let PREAMBLE = - `(memory 1) + let PREAMBLE = + `(memory${decltype} 1) (data "\\37")`; -// drop with no memory -print( + // drop with no memory + print( `(assert_invalid (module (func (export "test") @@ -68,8 +71,8 @@ print( "unknown data segment") `); -// drop with data seg ix out of range -print( + // drop with data seg ix out of range + print( `(assert_invalid (module ${PREAMBLE} @@ -78,8 +81,8 @@ print( "unknown data segment") `); -// drop, then drop -print( + // drop, then drop + print( `(module ${PREAMBLE} (func (export "test") @@ -88,147 +91,147 @@ print( (invoke "test") `); -// drop, then init -print( + // drop, then init + print( `(module ${PREAMBLE} (func (export "test") (data.drop 0) - (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) + (memory.init 0 (${memtype}.const 1234) (i32.const 1) (i32.const 1)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// init with data seg ix indicating an active segment -print( + // init with data seg ix indicating an active segment + print( `(module - (memory 1) - (data (i32.const 0) "\\37") + (memory${decltype} 1) + (data (${memtype}.const 0) "\\37") (func (export "test") - (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) + (memory.init 0 (${memtype}.const 1234) (i32.const 1) (i32.const 1)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// init with no memory -print( + // init with no memory + print( `(assert_invalid (module (func (export "test") - (memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1)))) + (memory.init 1 (${memtype}.const 1234) (i32.const 1) (i32.const 1)))) "unknown memory 0") `); -// init with data seg ix out of range -print( + // init with data seg ix out of range + print( `(assert_invalid (module ${PREAMBLE} (func (export "test") - (memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1)))) + (memory.init 1 (${memtype}.const 1234) (i32.const 1) (i32.const 1)))) "unknown data segment 1") `); -// init, using a data seg ix more than once is OK -print( + // init, using a data seg ix more than once is OK + print( `(module ${PREAMBLE} (func (export "test") - (memory.init 0 (i32.const 1) (i32.const 0) (i32.const 1)) - (memory.init 0 (i32.const 1) (i32.const 0) (i32.const 1)))) + (memory.init 0 (${memtype}.const 1) (i32.const 0) (i32.const 1)) + (memory.init 0 (${memtype}.const 1) (i32.const 0) (i32.const 1)))) (invoke "test") `); -// init: seg ix is valid passive, but length to copy > len of seg -print( + // init: seg ix is valid passive, but length to copy > len of seg + print( `(module ${PREAMBLE} (func (export "test") - (memory.init 0 (i32.const 1234) (i32.const 0) (i32.const 5)))) + (memory.init 0 (${memtype}.const 1234) (i32.const 0) (i32.const 5)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// init: seg ix is valid passive, but implies copying beyond end of seg -print( + // init: seg ix is valid passive, but implies copying beyond end of seg + print( `(module ${PREAMBLE} (func (export "test") - (memory.init 0 (i32.const 1234) (i32.const 2) (i32.const 3)))) + (memory.init 0 (${memtype}.const 1234) (i32.const 2) (i32.const 3)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// init: seg ix is valid passive, but implies copying beyond end of dst -print( + // init: seg ix is valid passive, but implies copying beyond end of dst + print( `(module ${PREAMBLE} (func (export "test") - (memory.init 0 (i32.const 0xFFFE) (i32.const 1) (i32.const 3)))) + (memory.init 0 (${memtype}.const 0xFFFE) (i32.const 1) (i32.const 3)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// init: seg ix is valid passive, src offset past the end, zero len is invalid -print( + // init: seg ix is valid passive, src offset past the end, zero len is invalid + print( `(module ${PREAMBLE} (func (export "test") - (memory.init 0 (i32.const 1234) (i32.const 4) (i32.const 0)))) + (memory.init 0 (${memtype}.const 1234) (i32.const 4) (i32.const 0)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// init: seg ix is valid passive, zero len, src offset at the end -print( + // init: seg ix is valid passive, zero len, src offset at the end + print( `(module ${PREAMBLE} (func (export "test") - (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 0)))) + (memory.init 0 (${memtype}.const 1234) (i32.const 1) (i32.const 0)))) (invoke "test") `); -// init: seg ix is valid passive, dst offset past the end, zero len is invalid -print( + // init: seg ix is valid passive, dst offset past the end, zero len is invalid + print( `(module ${PREAMBLE} (func (export "test") - (memory.init 0 (i32.const 0x10001) (i32.const 0) (i32.const 0)))) + (memory.init 0 (${memtype}.const 0x10001) (i32.const 0) (i32.const 0)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// init: seg ix is valid passive, zero len, but dst offset at the end -print( + // init: seg ix is valid passive, zero len, but dst offset at the end + print( `(module ${PREAMBLE} (func (export "test") - (memory.init 0 (i32.const 0x10000) (i32.const 0) (i32.const 0)))) + (memory.init 0 (${memtype}.const 0x10000) (i32.const 0) (i32.const 0)))) (invoke "test") `); -// init: seg ix is valid passive, zero len, dst and src offsets at the end -print( + // init: seg ix is valid passive, zero len, dst and src offsets at the end + print( `(module ${PREAMBLE} (func (export "test") - (memory.init 0 (i32.const 0x10000) (i32.const 1) (i32.const 0)))) + (memory.init 0 (${memtype}.const 0x10000) (i32.const 1) (i32.const 0)))) (invoke "test") `); -// init: seg ix is valid passive, src and dst offset past the end, zero len is -// invalid -print( + // init: seg ix is valid passive, src and dst offset past the end, zero len is + // invalid + print( `(module ${PREAMBLE} (func (export "test") - (memory.init 0 (i32.const 0x10001) (i32.const 4) (i32.const 0)))) + (memory.init 0 (${memtype}.const 0x10001) (i32.const 4) (i32.const 0)))) (assert_trap (invoke "test") "out of bounds memory access") `); -// invalid argument types. TODO: can add anyfunc etc here. -{ - const tys = ['i32', 'f32', 'i64', 'f64']; + // invalid argument types. TODO: can add anyfunc etc here. + { + const tys = ['i32', 'f32', 'i64', 'f64']; - for (let ty1 of tys) { - for (let ty2 of tys) { - for (let ty3 of tys) { - if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32') - continue; // this is the only valid case - print( + for (let ty1 of tys) { + for (let ty2 of tys) { + for (let ty3 of tys) { + if (ty1 == memtype && ty2 == 'i32' && ty3 == 'i32') + continue; // this is the only valid case + print( `(assert_invalid (module ${PREAMBLE} @@ -236,68 +239,68 @@ print( (memory.init 0 (${ty1}.const 1) (${ty2}.const 1) (${ty3}.const 1)))) "type mismatch") `); - }}} -} + }}} + } -// memory.init: out of bounds of the memory or the segment, but should perform -// the operation up to the appropriate bound. -// -// Arithmetic overflow of memoffset + len or of bufferoffset + len should not -// affect the behavior. + // memory.init: out of bounds of the memory or the segment, but should perform + // the operation up to the appropriate bound. + // + // Arithmetic overflow of memoffset + len or of bufferoffset + len should not + // affect the behavior. -// Note, the length of the data segment is 16. -const mem_init_len = 16; + // Note, the length of the data segment is 16. + const mem_init_len = 16; -function mem_init(min, max, shared, backup, write) { - print( + function mem_init(min, max, shared, backup, write) { + print( `(module - (memory ${min} ${max} ${shared}) + (memory${decltype} ${min} ${max} ${shared}) (data "\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42\\42") - ${checkRangeCode()} - (func (export "run") (param $offs i32) (param $len i32) + ${checkRangeCode(memtype)} + (func (export "run") (param $offs ${memtype}) (param $len i32) (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) `); - // A fill writing past the end of the memory should throw *and* have filled - // all the way up to the end. - // - // A fill reading past the end of the segment should throw *and* have filled - // memory with as much data as was available. - let offs = min*PAGESIZE - backup; - print( -`(assert_trap (invoke "run" (i32.const ${offs}) (i32.const ${write})) + // A fill writing past the end of the memory should throw *and* have filled + // all the way up to the end. + // + // A fill reading past the end of the segment should throw *and* have filled + // memory with as much data as was available. + let offs = min*PAGESIZE - backup; + print( +`(assert_trap (invoke "run" (${memtype}.const ${offs}) (i32.const ${write})) "out of bounds memory access") `); - checkRange(0, min, 0); -} - -// We exceed the bounds of the memory but not of the data segment -mem_init(1, 1, "", Math.floor(mem_init_len/2), mem_init_len); -mem_init(1, 1, "", Math.floor(mem_init_len/2)+1, mem_init_len); -if (WITH_SHARED_MEMORY) { - mem_init(2, 4, "shared", Math.floor(mem_init_len/2), mem_init_len); - mem_init(2, 4, "shared", Math.floor(mem_init_len/2)+1, mem_init_len); -} - -// We exceed the bounds of the data segment but not the memory -mem_init(1, 1, "", mem_init_len*4, mem_init_len*2-2); -mem_init(1, 1, "", mem_init_len*4-1, mem_init_len*2-1); -if (WITH_SHARED_MEMORY) { - mem_init(2, 4, "shared", mem_init_len*4, mem_init_len*2-2); - mem_init(2, 4, "shared", mem_init_len*4-1, mem_init_len*2-1); -} - -// We arithmetically overflow the memory limit but not the segment limit -mem_init(1, "", "", Math.floor(mem_init_len/2), 0xFFFFFF00); - -// We arithmetically overflow the segment limit but not the memory limit -mem_init(1, "", "", PAGESIZE, 0xFFFFFFFC); - -// Test that the data segment index is properly encoded as an unsigned (not -// signed) LEB. -print( + checkRange(memtype, 0, min, 0); + } + + // We exceed the bounds of the memory but not of the data segment + mem_init(1, 1, "", Math.floor(mem_init_len/2), mem_init_len); + mem_init(1, 1, "", Math.floor(mem_init_len/2)+1, mem_init_len); + if (WITH_SHARED_MEMORY) { + mem_init(2, 4, "shared", Math.floor(mem_init_len/2), mem_init_len); + mem_init(2, 4, "shared", Math.floor(mem_init_len/2)+1, mem_init_len); + } + + // We exceed the bounds of the data segment but not the memory + mem_init(1, 1, "", mem_init_len*4, mem_init_len*2-2); + mem_init(1, 1, "", mem_init_len*4-1, mem_init_len*2-1); + if (WITH_SHARED_MEMORY) { + mem_init(2, 4, "shared", mem_init_len*4, mem_init_len*2-2); + mem_init(2, 4, "shared", mem_init_len*4-1, mem_init_len*2-1); + } + + // We arithmetically overflow the memory limit but not the segment limit + mem_init(1, "", "", Math.floor(mem_init_len/2), 0xFFFFFF00); + + // We arithmetically overflow the segment limit but not the memory limit + mem_init(1, "", "", PAGESIZE, 0xFFFFFFFC); + + // Test that the data segment index is properly encoded as an unsigned (not + // signed) LEB. + print( ` (module - (memory 1) + (memory${decltype} 1) ;; 65 data segments. 64 is the smallest positive number that is encoded ;; differently as a signed LEB. (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") @@ -309,4 +312,5 @@ print( (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") - (func (memory.init 64 (i32.const 0) (i32.const 0) (i32.const 0))))`) + (func (memory.init 64 (${memtype}.const 0) (i32.const 0) (i32.const 0))))`) +} diff --git a/test/meta/generate_table_copy.js b/test/meta/generate_table_copy.js index f7fd47dcb..8fb0a9224 100644 --- a/test/meta/generate_table_copy.js +++ b/test/meta/generate_table_copy.js @@ -154,16 +154,17 @@ for ( let table of [0,1] ) { // Out-of-bounds checks. -function do_test(insn1, insn2, errText) +function do_test(tt, insn1, insn2, errText) { + const type = tt == 'i64' ? ' i64' : ''; print(` (module - (table $t0 30 30 funcref) - (table $t1 30 30 funcref) - (elem (table $t0) (i32.const 2) func 3 1 4 1) + (table $t0${type} 30 30 funcref) + (table $t1${type} 30 30 funcref) + (elem (table $t0) (${tt}.const 2) func 3 1 4 1) (elem funcref (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) - (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem (table $t0) (${tt}.const 12) func 7 5 2 3 6) (elem funcref (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) (func (result i32) (i32.const 0)) @@ -188,72 +189,74 @@ function do_test(insn1, insn2, errText) } } -function tab_test2(insn1, insn2, errKind, errText) { - do_test(insn1, insn2, errKind, errText); +function tab_test2(tt, insn1, insn2, errKind, errText) { + do_test(tt, insn1, insn2, errKind, errText); } -function tab_test_nofail(insn1, insn2) { - do_test(insn1, insn2, undefined, undefined); +function tab_test_nofail(tt, insn1, insn2) { + do_test(tt, insn1, insn2, undefined, undefined); } -for ( let dest of ["$t0","$t1"] ) { - // Here we test the boundary-failure cases. The table's valid indices are 0..29 - // inclusive. - - // copy: dst range invalid - tab_test2(`(table.copy ${dest} $t0 (i32.const 28) (i32.const 1) (i32.const 3))`, - "", - "out of bounds table access"); - - // copy: dst wraparound end of 32 bit offset space - tab_test2(`(table.copy ${dest} $t0 (i32.const 0xFFFFFFFE) (i32.const 1) (i32.const 2))`, - "", - "out of bounds table access"); - - // copy: src range invalid - tab_test2(`(table.copy ${dest} $t0 (i32.const 15) (i32.const 25) (i32.const 6))`, - "", - "out of bounds table access"); - - // copy: src wraparound end of 32 bit offset space - tab_test2(`(table.copy ${dest} $t0 (i32.const 15) (i32.const 0xFFFFFFFE) (i32.const 2))`, - "", - "out of bounds table access"); - - // copy: zero length with both offsets in-bounds is OK - tab_test_nofail( - `(table.copy ${dest} $t0 (i32.const 15) (i32.const 25) (i32.const 0))`, - ""); - - // copy: zero length with dst offset out of bounds at the end of the table is allowed - tab_test2(`(table.copy ${dest} $t0 (i32.const 30) (i32.const 15) (i32.const 0))`, - "", - undefined); - - // copy: zero length with dst offset out of bounds past the end of the table is not allowed - tab_test2(`(table.copy ${dest} $t0 (i32.const 31) (i32.const 15) (i32.const 0))`, - "", - "out of bounds table access"); - - // copy: zero length with src offset out of bounds at the end of the table is allowed - tab_test2(`(table.copy ${dest} $t0 (i32.const 15) (i32.const 30) (i32.const 0))`, - "", - undefined); - - // copy: zero length with src offset out of bounds past the end of the table is not allowed - tab_test2(`(table.copy ${dest} $t0 (i32.const 15) (i32.const 31) (i32.const 0))`, - "", - "out of bounds table access"); - - // copy: zero length with both dst and src offset out of bounds at the end of the table is allowed - tab_test2(`(table.copy ${dest} $t0 (i32.const 30) (i32.const 30) (i32.const 0))`, - "", - undefined); - - // copy: zero length with both dst and src offset out of bounds past the end of the table is not allowed - tab_test2(`(table.copy ${dest} $t0 (i32.const 31) (i32.const 31) (i32.const 0))`, - "", - "out of bounds table access"); +for ( let tt of ["i32", "i64"] ) { + for ( let dest of ["$t0","$t1"] ) { + // Here we test the boundary-failure cases. The table's valid indices are 0..29 + // inclusive. + + // copy: dst range invalid + tab_test2(tt, `(table.copy ${dest} $t0 (${tt}.const 28) (${tt}.const 1) (${tt}.const 3))`, + "", + "out of bounds table access"); + + // copy: dst wraparound end of 32 bit offset space + tab_test2(tt, `(table.copy ${dest} $t0 (${tt}.const 0xFFFFFFFE) (${tt}.const 1) (${tt}.const 2))`, + "", + "out of bounds table access"); + + // copy: src range invalid + tab_test2(tt, `(table.copy ${dest} $t0 (${tt}.const 15) (${tt}.const 25) (${tt}.const 6))`, + "", + "out of bounds table access"); + + // copy: src wraparound end of 32 bit offset space + tab_test2(tt, `(table.copy ${dest} $t0 (${tt}.const 15) (${tt}.const 0xFFFFFFFE) (${tt}.const 2))`, + "", + "out of bounds table access"); + + // copy: zero length with both offsets in-bounds is OK + tab_test_nofail(tt, + `(table.copy ${dest} $t0 (${tt}.const 15) (${tt}.const 25) (${tt}.const 0))`, + ""); + + // copy: zero length with dst offset out of bounds at the end of the table is allowed + tab_test2(tt, `(table.copy ${dest} $t0 (${tt}.const 30) (${tt}.const 15) (${tt}.const 0))`, + "", + undefined); + + // copy: zero length with dst offset out of bounds past the end of the table is not allowed + tab_test2(tt, `(table.copy ${dest} $t0 (${tt}.const 31) (${tt}.const 15) (${tt}.const 0))`, + "", + "out of bounds table access"); + + // copy: zero length with src offset out of bounds at the end of the table is allowed + tab_test2(tt, `(table.copy ${dest} $t0 (${tt}.const 15) (${tt}.const 30) (${tt}.const 0))`, + "", + undefined); + + // copy: zero length with src offset out of bounds past the end of the table is not allowed + tab_test2(tt, `(table.copy ${dest} $t0 (${tt}.const 15) (${tt}.const 31) (${tt}.const 0))`, + "", + "out of bounds table access"); + + // copy: zero length with both dst and src offset out of bounds at the end of the table is allowed + tab_test2(tt, `(table.copy ${dest} $t0 (${tt}.const 30) (${tt}.const 30) (${tt}.const 0))`, + "", + undefined); + + // copy: zero length with both dst and src offset out of bounds past the end of the table is not allowed + tab_test2(tt, `(table.copy ${dest} $t0 (${tt}.const 31) (${tt}.const 31) (${tt}.const 0))`, + "", + "out of bounds table access"); + } } // table.copy: out of bounds of the table for the source or target, but should diff --git a/test/meta/generate_table_init.js b/test/meta/generate_table_init.js index 16b9a8406..5debac4e4 100644 --- a/test/meta/generate_table_init.js +++ b/test/meta/generate_table_init.js @@ -27,6 +27,7 @@ function emit_a() { // the table entry is empty. function emit_b(insn, table) { + let tt = table == 2 ? 'i64' : 'i32'; print( ` (module @@ -38,10 +39,11 @@ function emit_b(insn, table) { (import "a" "ef4" (func (result i32))) ;; index 4 (table $t0 30 30 funcref) (table $t1 30 30 funcref) - (elem (table $t${table}) (i32.const 2) func 3 1 4 1) + (table $t2 i64 30 30 funcref) + (elem (table $t${table}) (${tt}.const 2) func 3 1 4 1) (elem funcref (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) - (elem (table $t${table}) (i32.const 12) func 7 5 2 3 6) + (elem (table $t${table}) (${tt}.const 12) func 7 5 2 3 6) (elem funcref (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) (func (result i32) (i32.const 5)) ;; index 5 @@ -51,7 +53,7 @@ function emit_b(insn, table) { (func (result i32) (i32.const 9)) ;; index 9 (func (export "test") ${insn}) - (func (export "check") (param i32) (result i32) + (func (export "check") (param ${tt}) (result i32) (call_indirect $t${table} (type 0) (local.get 0))) ) `); @@ -65,12 +67,13 @@ function emit_b(insn, table) { function tab_test(instruction, table, expected_result_vector) { emit_b(instruction, table); print(`(invoke "test")`); + let tt = table == 2 ? 'i64' : 'i32'; for (let i = 0; i < expected_result_vector.length; i++) { let expected = expected_result_vector[i]; if (expected === undefined) { - print(`(assert_trap (invoke "check" (i32.const ${i})) "uninitialized element")`); + print(`(assert_trap (invoke "check" (${tt}.const ${i})) "uninitialized element")`); } else { - print(`(assert_return (invoke "check" (i32.const ${i})) (i32.const ${expected}))`); + print(`(assert_return (invoke "check" (${tt}.const ${i})) (i32.const ${expected}))`); } } } @@ -81,28 +84,29 @@ emit_a(); // to count through the vector entries when debugging. let e = undefined; -for ( let table of [0, 1] ) { +for ( let table of [0, 1, 2] ) { + let tt = table == 2 ? 'i64' : 'i32'; // Passive init that overwrites all-null entries - tab_test(`(table.init $t${table} 1 (i32.const 7) (i32.const 0) (i32.const 4))`, + tab_test(`(table.init $t${table} 1 (${tt}.const 7) (i32.const 0) (i32.const 4))`, table, [e,e,3,1,4, 1,e,2,7,1, 8,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]); // Passive init that overwrites existing active-init-created entries - tab_test(`(table.init $t${table} 3 (i32.const 15) (i32.const 1) (i32.const 3))`, + tab_test(`(table.init $t${table} 3 (${tt}.const 15) (i32.const 1) (i32.const 3))`, table, [e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 9,2,7,e,e, e,e,e,e,e, e,e,e,e,e]); // Perform active and passive initialisation and then multiple copies tab_test( - `(table.init $t${table} 1 (i32.const 7) (i32.const 0) (i32.const 4)) + `(table.init $t${table} 1 (${tt}.const 7) (i32.const 0) (i32.const 4)) (elem.drop 1) - (table.init $t${table} 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (table.init $t${table} 3 (${tt}.const 15) (i32.const 1) (i32.const 3)) (elem.drop 3) - (table.copy $t${table} ${table} (i32.const 20) (i32.const 15) (i32.const 5)) - (table.copy $t${table} ${table} (i32.const 21) (i32.const 29) (i32.const 1)) - (table.copy $t${table} ${table} (i32.const 24) (i32.const 10) (i32.const 1)) - (table.copy $t${table} ${table} (i32.const 13) (i32.const 11) (i32.const 4)) - (table.copy $t${table} ${table} (i32.const 19) (i32.const 20) (i32.const 5))`, + (table.copy $t${table} ${table} (${tt}.const 20) (${tt}.const 15) (${tt}.const 5)) + (table.copy $t${table} ${table} (${tt}.const 21) (${tt}.const 29) (${tt}.const 1)) + (table.copy $t${table} ${table} (${tt}.const 24) (${tt}.const 10) (${tt}.const 1)) + (table.copy $t${table} ${table} (${tt}.const 13) (${tt}.const 11) (${tt}.const 4)) + (table.copy $t${table} ${table} (${tt}.const 19) (${tt}.const 20) (${tt}.const 5))`, table, [e,e,3,1,4, 1,e,2,7,1, 8,e,7,e,7, 5,2,7,e,9, e,7,e,8,8, e,e,e,e,e]); }