From 92af25a5859b95d6c76e5de5cdd90008dc564b9f Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Tue, 20 Apr 2021 16:50:05 -0700 Subject: [PATCH] resolve conflict, replace two primary map to hash map --- .cargo/config.toml | 8 - .github/workflows/documentation.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/main.yaml | 14 +- CHANGELOG.md | 5 + Cargo.lock | 15 +- Makefile | 121 +- README.md | 6 +- benches/static_and_dynamic_functions.rs | 5 +- docs/cn/README.md | 4 +- docs/es/README.md | 4 +- docs/fr/README.md | 4 +- docs/ja/README.md | 191 + examples/table.rs | 6 +- fuzz/Cargo.lock | 4 +- fuzzbuzz.yaml | 9 + lib/api/Cargo.toml | 5 + lib/api/src/externals/function.rs | 79 +- lib/api/src/externals/global.rs | 2 +- lib/api/src/externals/table.rs | 27 +- lib/api/src/lib.rs | 6 +- lib/api/src/types.rs | 78 +- lib/api/tests/externals.rs | 10 +- lib/api/tests/reference_types.rs | 497 +++ lib/c-api/CHANGELOG.md | 22 + lib/c-api/Cargo.toml | 3 +- lib/c-api/examples/exports-function.c | 1 - lib/c-api/examples/exports-global.c | 2 - lib/c-api/examples/imports-exports.c | 4 - lib/c-api/examples/memory.c | 4 - lib/c-api/src/deprecated/table.rs | 2 +- .../src/wasm_c_api/externals/function.rs | 35 +- lib/c-api/src/wasm_c_api/externals/global.rs | 22 +- lib/c-api/src/wasm_c_api/externals/memory.rs | 22 +- lib/c-api/src/wasm_c_api/externals/mod.rs | 245 +- lib/c-api/src/wasm_c_api/externals/table.rs | 20 +- lib/c-api/src/wasm_c_api/instance.rs | 8 +- lib/c-api/src/wasm_c_api/types/export.rs | 11 +- lib/c-api/src/wasm_c_api/types/extern_.rs | 4 +- lib/c-api/src/wasm_c_api/types/import.rs | 25 +- lib/c-api/src/wasm_c_api/types/mod.rs | 53 +- lib/c-api/src/wasm_c_api/unstable/wasi.rs | 18 +- lib/c-api/src/wasm_c_api/wasi/mod.rs | 12 +- lib/c-api/tests/test-early-exit.c | 2 - lib/cli/src/commands/config.rs | 12 +- lib/cli/src/store.rs | 19 +- lib/compiler-cranelift/src/config.rs | 3 + lib/compiler-cranelift/src/func_environ.rs | 520 ++- .../src/translator/code_translator.rs | 454 ++- .../src/translator/func_environ.rs | 45 +- .../src/translator/func_state.rs | 88 +- .../src/translator/func_translator.rs | 27 +- lib/compiler-llvm/src/abi/aarch64_systemv.rs | 25 +- lib/compiler-llvm/src/abi/x86_64_systemv.rs | 40 +- lib/compiler-llvm/src/compiler.rs | 5 +- lib/compiler-llvm/src/object_file.rs | 58 +- lib/compiler-llvm/src/translator/code.rs | 331 +- .../src/translator/intrinsics.rs | 196 +- lib/compiler-llvm/src/translator/state.rs | 16 + lib/compiler-singlepass/src/codegen_x64.rs | 400 ++- lib/compiler-singlepass/src/machine.rs | 3 +- lib/compiler/src/translator/environ.rs | 32 +- lib/compiler/src/translator/sections.rs | 3 +- lib/deprecated/runtime-core/Cargo.lock | 115 +- lib/deprecated/runtime-core/src/table.rs | 2 +- lib/emscripten/src/env/unix/mod.rs | 33 +- lib/emscripten/src/env/windows/mod.rs | 20 +- lib/emscripten/src/lib.rs | 33 +- lib/emscripten/src/syscalls/mod.rs | 6 +- lib/emscripten/src/syscalls/unix.rs | 48 +- lib/emscripten/src/syscalls/windows.rs | 4 +- lib/engine-jit/src/artifact.rs | 9 +- lib/engine-jit/src/engine.rs | 20 +- lib/engine-native/src/artifact.rs | 11 +- lib/engine-native/src/engine.rs | 21 +- lib/engine-object-file/src/artifact.rs | 23 +- lib/engine-object-file/src/engine.rs | 21 +- lib/engine/src/artifact.rs | 8 +- lib/engine/src/engine.rs | 5 +- lib/types/Cargo.toml | 3 + lib/types/src/extern_ref.rs | 295 ++ lib/types/src/features.rs | 25 +- lib/types/src/lib.rs | 6 +- lib/types/src/native.rs | 36 +- lib/types/src/ref.rs | 267 -- lib/types/src/types.rs | 9 +- lib/types/src/values.rs | 153 +- lib/vm/src/func_data_registry.rs | 121 + lib/vm/src/global.rs | 32 +- lib/vm/src/instance/mod.rs | 309 +- lib/vm/src/lib.rs | 5 +- lib/vm/src/libcalls.rs | 511 ++- lib/vm/src/module.rs | 14 +- lib/vm/src/table.rs | 151 +- lib/vm/src/trap/trapcode.rs | 4 +- lib/vm/src/vmcontext.rs | 163 +- lib/vm/src/vmoffsets.rs | 18 + tests/ignores.txt | 1 - tests/lib/engine-dummy/src/artifact.rs | 10 +- tests/lib/engine-dummy/src/engine.rs | 16 +- tests/lib/wast/Cargo.toml | 2 +- tests/lib/wast/src/spectest.rs | 2 +- tests/lib/wast/src/wast.rs | 84 +- tests/wast/spec/imports.wast | 52 +- tests/wast/spec/ref_func.wast | 115 + tests/wast/spec/ref_is_null.wast | 58 + tests/wast/spec/ref_null.wast | 10 + tests/wast/spec/table-sub.wast | 21 + tests/wast/spec/table.wast | 4 +- tests/wast/spec/table_copy.wast | 3082 +++++++++++++++++ tests/wast/spec/table_fill.wast | 153 + tests/wast/spec/table_get.wast | 88 + tests/wast/spec/table_grow.wast | 173 + tests/wast/spec/table_init.wast | 2143 ++++++++++++ tests/wast/spec/table_set.wast | 119 + tests/wast/spec/table_size.wast | 86 + tests/wast/wasmer/rotate-shift-overflow.wast | 274 +- 117 files changed, 11350 insertions(+), 1540 deletions(-) create mode 100644 docs/ja/README.md create mode 100644 fuzzbuzz.yaml create mode 100644 lib/api/tests/reference_types.rs create mode 100644 lib/c-api/CHANGELOG.md create mode 100644 lib/types/src/extern_ref.rs delete mode 100644 lib/types/src/ref.rs create mode 100644 lib/vm/src/func_data_registry.rs create mode 100644 tests/wast/spec/ref_func.wast create mode 100644 tests/wast/spec/ref_is_null.wast create mode 100644 tests/wast/spec/ref_null.wast create mode 100644 tests/wast/spec/table-sub.wast create mode 100644 tests/wast/spec/table_copy.wast create mode 100644 tests/wast/spec/table_fill.wast create mode 100644 tests/wast/spec/table_get.wast create mode 100644 tests/wast/spec/table_grow.wast create mode 100644 tests/wast/spec/table_init.wast create mode 100644 tests/wast/spec/table_set.wast create mode 100644 tests/wast/spec/table_size.wast diff --git a/.cargo/config.toml b/.cargo/config.toml index f12b69fcf..d83dcde74 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,11 +3,3 @@ rustflags = [ # Put the VM functions in the dynamic symbol table. "-C", "link-arg=-Wl,-E", ] - -[target.'cfg(target_os = "windows")'] -rustflags = [ - # Enable `libwasmer` to be statically linked against the C Runtime - # Libraries (CRT) to reduce the number of dependencies inside the - # DLL. - "-C", "target-feature=+crt-static", -] \ No newline at end of file diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml index 983e8da21..e0c534a30 100644 --- a/.github/workflows/documentation.yaml +++ b/.github/workflows/documentation.yaml @@ -18,7 +18,7 @@ jobs: - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: 1.49 + toolchain: "1.50" override: true - name: Install LLVM shell: bash diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index d0adfc066..637eef9ec 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -19,7 +19,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.49 + toolchain: "1.50" override: true components: rustfmt, clippy - name: Install LLVM (Linux) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index dfe46b1bd..ab6faadd3 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -41,26 +41,26 @@ jobs: include: - build: linux-x64 os: ubuntu-18.04 - rust: 1.49 + rust: "1.50" llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/11.x/linux-amd64.tar.gz' artifact_name: 'wasmer-linux-amd64' cross_compilation_artifact_name: 'cross_compiled_from_linux' run_integration_tests: true - build: macos-x64 os: macos-latest - rust: 1.49 + rust: "1.50" llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/11.x/darwin-amd64.tar.gz' artifact_name: 'wasmer-darwin-amd64' cross_compilation_artifact_name: 'cross_compiled_from_mac' run_integration_tests: true - build: macos-arm64 os: macos-11.0 - rust: 1.49 + rust: "1.50" target: aarch64-apple-darwin artifact_name: 'wasmer-darwin-arm64' - build: windows-x64 os: windows-latest - rust: 1.49 + rust: "1.50" # llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/11.x/windows-amd64.tar.gz' artifact_name: 'wasmer-windows-amd64' cross_compilation_artifact_name: 'cross_compiled_from_win' @@ -68,7 +68,7 @@ jobs: - build: linux-aarch64 os: [self-hosted, linux, ARM64] random_sccache_port: true - rust: 1.49 + rust: "1.50" llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/11.x/linux-aarch64.tar.gz' artifact_name: 'wasmer-linux-aarch64' run_integration_tests: false @@ -289,12 +289,12 @@ jobs: include: - build: linux-musl-x64 image: alpine:latest - rust: 1.49 + rust: "1.50" artifact_name: 'wasmer-linux-musl-amd64' steps: - uses: actions/checkout@v2 - - uses: addnab/docker-run-action@v1 + - uses: addnab/docker-run-action@v3 with: image: ${{ matrix.image }} options: -v ${{ github.workspace }}:/work diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b9226707..09ba0f765 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,13 @@ [Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ +Looking for changes that affect our C API? See the [C API Changelog](lib/c-api/CHANGELOG.md). + ## **[Unreleased]** ### Added +- [#2208](https://github.com/wasmerio/wasmer/pull/2208) Add a new CHANGELOG.md specific to our C API to make it easier for users primarily consuming our C API to keep up to date with changes that affect them. - [#2103](https://github.com/wasmerio/wasmer/pull/2103) Add middleware (incl. metering) in the C API. - [#2003](https://github.com/wasmerio/wasmer/pull/2003) Wasmer works with musl, and is built, tested and packaged for musl. - [#2116](https://github.com/wasmerio/wasmer/pull/2116) Add a package for Windows that is not an installer, but all the `lib` and `include` files as for macOS and Linux. @@ -27,6 +30,8 @@ - [#2149](https://github.com/wasmerio/wasmer/pull/2144) `wasmer-engine-native` looks for clang-11 instead of clang-10. ### Fixed +- [#2208](https://github.com/wasmerio/wasmer/pull/2208) Fix ownership in Wasm C API of `wasm_extern_as_func`, `wasm_extern_as_memory`, `wasm_extern_as_table`, `wasm_extern_as_global`, `wasm_func_as_extern`, `wasm_memory_as_extern`, `wasm_table_as_extern`, and `wasm_global_as_extern`. These functions no longer allocate memory and thus their results should not be freed. This is a breaking change to align more closely with the Wasm C API's stated ownership. +- [#2210](https://github.com/wasmerio/wasmer/pull/2210) Fix a memory leak in the Wasm C API in the strings used to identify imports and exports coming from user code. - [#2108](https://github.com/wasmerio/wasmer/pull/2108) The Object Native Engine generates code that now compiles correctly with C++. - [#2125](https://github.com/wasmerio/wasmer/pull/2125) Fix RUSTSEC-2021-0023. - [#2155](https://github.com/wasmerio/wasmer/pull/2155) Fix the implementation of shift and rotate in the LLVM compiler. diff --git a/Cargo.lock b/Cargo.lock index dd5bf9d07..6713d7c57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -770,6 +770,16 @@ dependencies = [ "log", ] +[[package]] +name = "field-offset" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf539fba70056b50f40a22e0da30639518a12ee18c35807858a63b158cb6dde7" +dependencies = [ + "memoffset", + "rustc_version 0.3.3", +] + [[package]] name = "filetime" version = "0.2.14" @@ -1162,9 +1172,9 @@ dependencies = [ [[package]] name = "loupe" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1acf065b51eb58abbc66a07c27ec63142205d339a9f5093dc3e1d7cda3d5c9a3" +checksum = "d79b0cc3aa7552a59274f642a0a6e7419b7f5438aba06a0a82825918ba69f0e6" dependencies = [ "indexmap", "loupe-derive", @@ -2434,6 +2444,7 @@ dependencies = [ "cbindgen", "cfg-if 1.0.0", "enumset", + "field-offset", "inline-c", "lazy_static", "libc", diff --git a/Makefile b/Makefile index 4ceb72a25..f748da5b8 100644 --- a/Makefile +++ b/Makefile @@ -366,9 +366,9 @@ build-wasmer-debug: # incremental = false # codegen-units = 1 # rpath = false -build-wasmer-headless-minimal: RUSTFLAGS += "-C panic=abort" +build-wasmer-headless-minimal: RUSTFLAGS += -C panic=abort build-wasmer-headless-minimal: - RUSTFLAGS=${RUSTFLAGS} xargo build --target $(HOST_TARGET) --release --manifest-path=lib/cli/Cargo.toml --no-default-features --features headless-minimal --bin wasmer-headless + RUSTFLAGS="${RUSTFLAGS}" xargo build --target $(HOST_TARGET) --release --manifest-path=lib/cli/Cargo.toml --no-default-features --features headless-minimal --bin wasmer-headless ifeq ($(IS_DARWIN), 1) strip -u target/$(HOST_TARGET)/release/wasmer-headless else @@ -379,7 +379,7 @@ else endif endif -WAPM_VERSION = master # v0.5.0 +WAPM_VERSION = v0.5.1 get-wapm: [ -d "wapm-cli" ] || git clone --branch $(WAPM_VERSION) https://github.com/wasmerio/wapm-cli.git @@ -394,82 +394,87 @@ endif build-docs: cargo doc --release $(compiler_features) --document-private-items --no-deps --workspace -build-docs-capi: - cd lib/c-api/doc/deprecated/ && doxygen doxyfile - cargo doc --manifest-path lib/c-api/Cargo.toml --no-deps --features wat,jit,object-file,native,cranelift,wasi $(capi_default_features) +capi-setup: +ifeq ($(IS_WINDOWS), 1) + RUSTFLAGS += -C target-feature=+crt-static +endif -build-capi: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-docs-capi: capi-setup + cd lib/c-api/doc/deprecated/ && doxygen doxyfile + RUSTFLAGS="${RUSTFLAGS}" cargo doc --manifest-path lib/c-api/Cargo.toml --no-deps --features wat,jit,object-file,native,cranelift,wasi $(capi_default_features) + +build-capi: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,jit,native,object-file,wasi,middlewares $(capi_default_features) $(capi_compiler_features) -build-capi-singlepass: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-singlepass: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,jit,native,object-file,singlepass,wasi,middlewares $(capi_default_features) -build-capi-singlepass-jit: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-singlepass-jit: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,jit,singlepass,wasi,middlewares $(capi_default_features) -build-capi-singlepass-native: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-singlepass-native: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,native,singlepass,wasi,middlewares $(capi_default_features) -build-capi-singlepass-object-file: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-singlepass-object-file: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,object-file,singlepass,wasi,middlewares $(capi_default_features) -build-capi-cranelift: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-cranelift: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,jit,native,object-file,cranelift,wasi,middlewares $(capi_default_features) -build-capi-cranelift-system-libffi: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-cranelift-system-libffi: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,jit,native,object-file,cranelift,wasi,middlewares,system-libffi $(capi_default_features) -build-capi-cranelift-jit: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-cranelift-jit: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,jit,cranelift,wasi,middlewares $(capi_default_features) -build-capi-cranelift-native: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-cranelift-native: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,native,cranelift,wasi,middlewares $(capi_default_features) -build-capi-cranelift-object-file: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-cranelift-object-file: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,native,object-file,cranelift,wasi,middlewares $(capi_default_features) -build-capi-llvm: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-llvm: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,jit,native,object-file,llvm,wasi,middlewares $(capi_default_features) -build-capi-llvm-jit: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-llvm-jit: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,jit,llvm,wasi,middlewares $(capi_default_features) -build-capi-llvm-native: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-llvm-native: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,native,llvm,wasi,middlewares $(capi_default_features) -build-capi-llvm-object-file: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-llvm-object-file: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features deprecated,wat,object-file,llvm,wasi,middlewares $(capi_default_features) # Headless (we include the minimal to be able to run) -build-capi-headless-jit: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-headless-jit: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features jit,wasi -build-capi-headless-native: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-headless-native: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features native,wasi -build-capi-headless-object-file: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-headless-object-file: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features object-file,wasi -build-capi-headless-all: - cargo build --manifest-path lib/c-api/Cargo.toml --release \ +build-capi-headless-all: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features jit,native,object-file,wasi ########### @@ -724,23 +729,23 @@ install-wasmer-headless-minimal: update-testsuite: git subtree pull --prefix tests/wast/spec https://github.com/WebAssembly/testsuite.git master --squash -lint-packages: RUSTFLAGS += "-D dead-code -D nonstandard-style -D unused-imports -D unused-mut -D unused-variables -D unused-unsafe -D unreachable-patterns -D bad-style -D improper-ctypes -D unused-allocation -D unused-comparisons -D while-true -D unconditional-recursion -D bare-trait-objects" # TODO: add `-D missing-docs` # TODO: add `-D function_item_references` (not available on Rust 1.47, try when upgrading) +lint-packages: RUSTFLAGS += -D dead-code -D nonstandard-style -D unused-imports -D unused-mut -D unused-variables -D unused-unsafe -D unreachable-patterns -D bad-style -D improper-ctypes -D unused-allocation -D unused-comparisons -D while-true -D unconditional-recursion -D bare-trait-objects # TODO: add `-D missing-docs` # TODO: add `-D function_item_references` (not available on Rust 1.47, try when upgrading) lint-packages: - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-c-api - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-vm - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-types - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-wasi - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-object - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-engine-native - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-engine-jit - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-compiler - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-compiler-cranelift - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-compiler-singlepass - RUSTFLAGS=${RUSTFLAGS} cargo clippy --manifest-path lib/cli/Cargo.toml $(compiler_features) - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-cache - RUSTFLAGS=${RUSTFLAGS} cargo clippy -p wasmer-engine - RUSTFLAGS=${RUSTFLAGS} cargo clippy --manifest-path fuzz/Cargo.toml $(compiler_features) + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-c-api + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-vm + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-types + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-wasi + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-object + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-engine-native + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-engine-jit + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-compiler + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-compiler-cranelift + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-compiler-singlepass + RUSTFLAGS="${RUSTFLAGS}" cargo clippy --manifest-path lib/cli/Cargo.toml $(compiler_features) + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-cache + RUSTFLAGS="${RUSTFLAGS}" cargo clippy -p wasmer-engine + RUSTFLAGS="${RUSTFLAGS}" cargo clippy --manifest-path fuzz/Cargo.toml $(compiler_features) lint-formatting: cargo fmt --all -- --check diff --git a/README.md b/README.md index dd952cc15..f00a98ea2 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ [Wasmer](https://wasmer.io/) enables super lightweight containers based on [WebAssembly](https://webassembly.org/) that can run anywhere: from Desktop to the Cloud and IoT devices, and also embedded in [*any programming language*](https://github.com/wasmerio/wasmer#language-integrations). -> This readme is also available in: [🇨🇳 中文-Chinese](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) • [🇪🇸 Español-Spanish](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) • [🇫🇷 Français-French](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md). +> This readme is also available in: [🇨🇳 中文-Chinese](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) • [🇪🇸 Español-Spanish](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) • [🇫🇷 Français-French](https://github.com/wasmerio/wasmer/blob/master/docs/fr/README.md) • [🇯🇵 日本語-Japanese](https://github.com/wasmerio/wasmer/blob/master/docs/ja/README.md). ## Features @@ -66,7 +66,7 @@ iwr https://win.wasmer.io -useb | iex #### Executing a WebAssembly file -After installing Wasmer you should be ready to execute your first WebAssemby file! 🎉 +After installing Wasmer you should be ready to execute your first WebAssembly file! 🎉 You can start by running QuickJS: [qjs.wasm](https://registry-cdn.wapm.io/contents/_/quickjs/0.0.3/build/qjs.wasm) @@ -108,7 +108,7 @@ qjs > [rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg [rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api [`wasmer` rust crate]: https://crates.io/crates/wasmer/ -[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer_runtime +[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer [c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg [c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api diff --git a/benches/static_and_dynamic_functions.rs b/benches/static_and_dynamic_functions.rs index 74e62f2a5..fc27b3240 100644 --- a/benches/static_and_dynamic_functions.rs +++ b/benches/static_and_dynamic_functions.rs @@ -1,7 +1,6 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use wasmer::*; -use wasmer_engine_jit::JIT; static BASIC_WAT: &str = r#"(module (func $multiply (import "env" "multiply") (param i32 i32) (result i32)) @@ -147,7 +146,7 @@ pub fn run_basic_dynamic_function(store: &Store, compiler_name: &str, c: &mut Cr ); } -fn run_static_benchmarks(c: &mut Criterion) { +fn run_static_benchmarks(_c: &mut Criterion) { #[cfg(feature = "llvm")] { let store = Store::new(&JIT::new(wasmer_compiler_llvm::LLVM::new()).engine()); @@ -167,7 +166,7 @@ fn run_static_benchmarks(c: &mut Criterion) { } } -fn run_dynamic_benchmarks(c: &mut Criterion) { +fn run_dynamic_benchmarks(_c: &mut Criterion) { #[cfg(feature = "llvm")] { let store = Store::new(&JIT::new(wasmer_compiler_llvm::LLVM::new()).engine()); diff --git a/docs/cn/README.md b/docs/cn/README.md index 8ccbe74e1..3e9105f11 100644 --- a/docs/cn/README.md +++ b/docs/cn/README.md @@ -29,7 +29,7 @@ [Wasmer](https://wasmer.io/) 使得能够基于 [WebAssembly](https://webassembly.org/),其可以在任何地方运行超轻型容器:从桌面到云和的IoT装置,并且也嵌入在 [*任何编程语言*](https://github.com/wasmerio/wasmer#language-integrations). -> This readme is also available in: [🇬🇧 English-英文](https://github.com/wasmerio/wasmer/blob/master/README.md) • [🇪🇸 Español-西班牙语](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) • [🇫🇷 Français-法语/法语](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md). +> This readme is also available in: [🇬🇧 English-英文](https://github.com/wasmerio/wasmer/blob/master/README.md) • [🇪🇸 Español-西班牙语](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) • [🇫🇷 Français-法语](https://github.com/wasmerio/wasmer/blob/master/docs/fr/README.md) • [🇯🇵 日本語-日文](https://github.com/wasmerio/wasmer/blob/master/docs/ja/README.md). ## 特征 @@ -108,7 +108,7 @@ qjs > [rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg [rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api [`wasmer` rust crate]: https://crates.io/crates/wasmer/ -[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer_runtime +[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer [c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg [c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api diff --git a/docs/es/README.md b/docs/es/README.md index 8a6924a9f..970092ce9 100644 --- a/docs/es/README.md +++ b/docs/es/README.md @@ -29,7 +29,7 @@ [Wasmer](https://wasmer.io/) hace posible tener contenedores ultraligeros basados en [WebAssembly](https://webassembly.org/) que pueden ser ejecutados en cualquier sitio: desde tu ordenador hasta la nube y dispositivos de IoT, además de poder ser ejecutados [*en cualquier lenguaje de programación*](https://github.com/wasmerio/wasmer#language-integrations). -> This README is also available in: [🇬🇧 English-Inglés](https://github.com/wasmerio/wasmer/blob/master/README.md) • [🇫🇷 Français-Francés](https://github.com/wasmerio/wasmer/blob/master/docs/fr/README.md) • [🇨🇳 中文-Chino](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md). +> This README is also available in: [🇬🇧 English-Inglés](https://github.com/wasmerio/wasmer/blob/master/README.md) • [🇫🇷 Français-Francés](https://github.com/wasmerio/wasmer/blob/master/docs/fr/README.md) • [🇨🇳 中文-Chino](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) • [🇯🇵 日本語-japonés](https://github.com/wasmerio/wasmer/blob/master/docs/ja/README.md). ## Funcionalidades @@ -107,7 +107,7 @@ qjs > [rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg [rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api [`wasmer` en crates.io]: https://crates.io/crates/wasmer/ -[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer_runtime +[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer [c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg [c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api diff --git a/docs/fr/README.md b/docs/fr/README.md index e6f87a90e..67b97711d 100644 --- a/docs/fr/README.md +++ b/docs/fr/README.md @@ -29,7 +29,7 @@ [Wasmer](https://wasmer.io/) permet l'utilisation de conteneurs super légers basés sur [WebAssembly](https://webassembly.org/) qui peuvent fonctionner n'importe où : du bureau au cloud en passant par les appareils IoT, et également intégrés dans [*une multitude de langages de programmation*](https://github.com/wasmerio/wasmer#language-integrations). -> This readme is also available in: [🇬🇧 English-Anglaise](https://github.com/wasmerio/wasmer/blob/master/README.md) • [🇪🇸 Español-Espagnol](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) • [🇨🇳 中文-Chinoise](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) +> This readme is also available in: [🇬🇧 English-Anglaise](https://github.com/wasmerio/wasmer/blob/master/README.md) • [🇪🇸 Español-Espagnol](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) • [🇨🇳 中文-Chinoise](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) • [🇯🇵 日本語-japonais](https://github.com/wasmerio/wasmer/blob/master/docs/ja/README.md) ## Fonctionnalités @@ -107,7 +107,7 @@ qjs > [rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg [rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api [`wasmer` rust crate]: https://crates.io/crates/wasmer/ -[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer_runtime +[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer [c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg [c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api diff --git a/docs/ja/README.md b/docs/ja/README.md new file mode 100644 index 000000000..c937407d9 --- /dev/null +++ b/docs/ja/README.md @@ -0,0 +1,191 @@ +
+ + Wasmerロゴ + + +

+ + ビルドステータス + + + ライセンス + + + Slackチャンネル + +

+ +

+ Website + + Docs + + Chat +

+ +
+ +
+ +[Wasmer](https://wasmer.io/) は、[WebAssembly](https://webassembly.org/) をベースとした非常に軽量なコンテナを実現します。デスクトップからクラウドや IoT デバイス上まで、どんな環境でも実行でき、さらに[*任意のプログラミング言語*](#他の言語とのインテグレーション)に埋め込むこともできます。 + +> この readme は、次の言語でも利用可能です。[🇨🇳 中文-Chinese](https://github.com/wasmerio/wasmer/blob/master/docs/cn/README.md) • [🇬🇧 英語-English](https://github.com/wasmerio/wasmer/blob/master/README.md) • [🇪🇸 Español-Spanish](https://github.com/wasmerio/wasmer/blob/master/docs/es/README.md) • [🇫🇷 Français-French](https://github.com/wasmerio/wasmer/blob/master/docs/fr/README.md) + +## 機能 + +* **高速かつ安全**。WebAssembly を完全なサンドボックス環境内で*ネイティブに近い*スピードで実行します。 + +* **プラガブル**。異なるコンパイルフレームワーク (LLVM、Cranelift など...) をサポートしているため、ニーズに合った最適なフレームワークを選択できます。 + +* **ユニバーサル**。どんなプラットフォーム上 (macOS、Linux、Windows) でも、どんな*チップセット*上でも実行できます。 + +* **標準に準拠**。ランタイムは[公式の WebAssembly テストスイート](https://github.com/WebAssembly/testsuite)に通っており、[WASI](https://github.com/WebAssembly/WASI) と [Emscripten](https://emscripten.org/) をサポートします。 + +## クイックスタート + +Wasmer は依存関係なしで動作します。以下のコマンドでインストーラーを使用してインストールできます。 + +```sh +curl https://get.wasmer.io -sSfL | sh +``` + +
+ PowerShell の場合 (Windows) +

+ +```powershell +iwr https://win.wasmer.io -useb | iex +``` + +

+
+ +> Homebrew、Scoop、Cargo など、他のインストール方法については、[wasmer-install](https://github.com/wasmerio/wasmer-install) を参照してください。 + + +#### WebAssembly ファイルの実行 + +Wasmer をインストールしたら、初めての WebAssembly ファイルの実行準備が完了です! 🎉 + +QuickJS ([qjs.wasm](https://registry-cdn.wapm.io/contents/_/quickjs/0.0.3/build/qjs.wasm)) を実行することで、すぐに始められます。 + +```bash +$ wasmer qjs.wasm +QuickJS - Type "\h" for help +qjs > +``` + +#### 次にできること + +- [Rust アプリケーションから Wasmer を使用する](https://docs.wasmer.io/integrations/rust) +- [WAPM で Wasm パッケージを公開する](https://docs.wasmer.io/ecosystem/wapm/publishing-your-package) +- [Wasmer についてさらに学ぶ](https://medium.com/wasmer/) + +## 他の言語とのインテグレーション + +📦 Wasmer ランタイムは**他の言語に組み込んで**使用できるため、WebAssembly は_どんな場所でも_利用できます。 + +|   | Language | Package | Docs | +|-|-|-|-| +| ![Rust logo] | [**Rust**][Rust integration] | [`wasmer` Rust crate] | [Docs][rust docs] +| ![C logo] | [**C/C++**][C integration] | [`wasmer.h` headers] | [Docs][c docs] | +| ![C# logo] | [**C#**][C# integration] | [`WasmerSharp` NuGet package] | [Docs][c# docs] | +| ![D logo] | [**D**][D integration] | [`wasmer` Dub package] | [Docs][d docs] | +| ![Python logo] | [**Python**][Python integration] | [`wasmer` PyPI package] | [Docs][python docs] | +| ![JS logo] | [**Javascript**][JS integration] | [`@wasmerio` NPM packages] | [Docs][js docs] | +| ![Go logo] | [**Go**][Go integration] | [`wasmer` Go package] | [Docs][go docs] | +| ![PHP logo] | [**PHP**][PHP integration] | [`wasm` PECL package] | [Docs][php docs] | +| ![Ruby logo] | [**Ruby**][Ruby integration] | [`wasmer` Ruby Gem] | [Docs][ruby docs] | +| ![Java logo] | [**Java**][Java integration] | [`wasmer/wasmer-jni` Bintray package] | [Docs][java docs] | +| ![Elixir logo] | [**Elixir**][Elixir integration] | [`wasmex` hex package] | [Docs][elixir docs] | +| ![R logo] | [**R**][R integration] | *公開パッケージなし* | [Docs][r docs] | +| ![Postgres logo] | [**Postgres**][Postgres integration] | *公開パッケージなし* | [Docs][postgres docs] | +| | [**Swift**][Swift integration] | *公開パッケージなし* | | + +[👋 言語が見当たらない?](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) + +[rust logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/rust.svg +[rust integration]: https://github.com/wasmerio/wasmer/tree/master/lib/api +[`wasmer` rust crate]: https://crates.io/crates/wasmer/ +[rust docs]: https://wasmerio.github.io/wasmer/crates/wasmer + +[c logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/c.svg +[c integration]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api +[`wasmer.h` headers]: https://wasmerio.github.io/wasmer/c/ +[c docs]: https://wasmerio.github.io/wasmer/c/ + +[c# logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/csharp.svg +[c# integration]: https://github.com/migueldeicaza/WasmerSharp +[`wasmersharp` nuget package]: https://www.nuget.org/packages/WasmerSharp/ +[c# docs]: https://migueldeicaza.github.io/WasmerSharp/ + +[d logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/d.svg +[d integration]: https://github.com/chances/wasmer-d +[`wasmer` Dub package]: https://code.dlang.org/packages/wasmer +[d docs]: https://chances.github.io/wasmer-d + +[python logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/python.svg +[python integration]: https://github.com/wasmerio/wasmer-python +[`wasmer` pypi package]: https://pypi.org/project/wasmer/ +[python docs]: https://github.com/wasmerio/wasmer-python#api-of-the-wasmer-extensionmodule + +[go logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/go.svg +[go integration]: https://github.com/wasmerio/wasmer-go +[`wasmer` go package]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer +[go docs]: https://pkg.go.dev/github.com/wasmerio/wasmer-go/wasmer?tab=doc + +[php logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/php.svg +[php integration]: https://github.com/wasmerio/wasmer-php +[`wasm` pecl package]: https://pecl.php.net/package/wasm +[php docs]: https://wasmerio.github.io/wasmer-php/wasm/ + +[js logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/js.svg +[js integration]: https://github.com/wasmerio/wasmer-js +[`@wasmerio` npm packages]: https://www.npmjs.com/org/wasmer +[js docs]: https://docs.wasmer.io/integrations/js/reference-api + +[ruby logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/ruby.svg +[ruby integration]: https://github.com/wasmerio/wasmer-ruby +[`wasmer` ruby gem]: https://rubygems.org/gems/wasmer +[ruby docs]: https://www.rubydoc.info/gems/wasmer/ + +[java logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/java.svg +[java integration]: https://github.com/wasmerio/wasmer-java +[`wasmer/wasmer-jni` bintray package]: https://bintray.com/wasmer/wasmer-jni/wasmer-jni +[java docs]: https://github.com/wasmerio/wasmer-java/#api-of-the-wasmer-library + +[elixir logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/elixir.svg +[elixir integration]: https://github.com/tessi/wasmex +[elixir docs]: https://hexdocs.pm/wasmex/api-reference.html +[`wasmex` hex package]: https://hex.pm/packages/wasmex + +[r logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/r.svg +[r integration]: https://github.com/dirkschumacher/wasmr +[r docs]: https://github.com/dirkschumacher/wasmr#example + +[postgres logo]: https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/languages/postgres.svg +[postgres integration]: https://github.com/wasmerio/wasmer-postgres +[postgres docs]: https://github.com/wasmerio/wasmer-postgres#usage--documentation + +[swift integration]: https://github.com/AlwaysRightInstitute/SwiftyWasmer + +## コントリビューション + +**どんな形での貢献も歓迎です。コミュニティの新しいメンバーからの貢献は特に歓迎します。** 💜 + +Wasmer ランタイムのビルド方法は、[素晴らしいドキュメント](https://docs.wasmer.io/ecosystem/wasmer/building-from-source)で確認できます! + +### テスト + +テストを実行したいですか? [Wasmer docs で方法を説明](https://docs.wasmer.io/ecosystem/wasmer/building-from-source/testing)しています。 + +## コミュニティ + +Wasmer には、開発者とコントリビューターの素晴らしいコミュニティがあります。ようこそ! あなたも是非参加してください! 👋 + +### チャンネル + +- [Slack](https://slack.wasmer.io/) +- [Twitter](https://twitter.com/wasmerio) +- [Facebook](https://www.facebook.com/wasmerio) +- [Email](mailto:hello@wasmer.io) diff --git a/examples/table.rs b/examples/table.rs index 90012a6b3..89f935910 100644 --- a/examples/table.rs +++ b/examples/table.rs @@ -116,9 +116,9 @@ fn main() -> anyhow::Result<()> { maximum: Some(6), } ); - // Now demonstarte that the function we grew the table with is actually in the table. + // Now demonstrate that the function we grew the table with is actually in the table. for table_index in 3..6 { - if let Value::FuncRef(f) = guest_table.get(table_index as _).unwrap() { + if let Value::FuncRef(Some(f)) = guest_table.get(table_index as _).unwrap() { let result = f.call(&[Value::I32(1), Value::I32(9)])?; assert_eq!(result[0], Value::I32(10)); } else { @@ -140,7 +140,7 @@ fn main() -> anyhow::Result<()> { // Now demonstrate that the host and guest see the same table and that both // get the same result. for table_index in 3..6 { - if let Value::FuncRef(f) = guest_table.get(table_index as _).unwrap() { + if let Value::FuncRef(Some(f)) = guest_table.get(table_index as _).unwrap() { let result = f.call(&[Value::I32(1), Value::I32(9)])?; assert_eq!(result[0], Value::I32(10)); } else { diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index d19cdc2c9..da12f7c70 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -511,9 +511,9 @@ dependencies = [ [[package]] name = "loupe" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1acf065b51eb58abbc66a07c27ec63142205d339a9f5093dc3e1d7cda3d5c9a3" +checksum = "d79b0cc3aa7552a59274f642a0a6e7419b7f5438aba06a0a82825918ba69f0e6" dependencies = [ "indexmap", "loupe-derive", diff --git a/fuzzbuzz.yaml b/fuzzbuzz.yaml new file mode 100644 index 000000000..7a40bac38 --- /dev/null +++ b/fuzzbuzz.yaml @@ -0,0 +1,9 @@ +wasmer: + language: rust + features: + - jit + - cranelift + - singlepass + deps: + - run: apt update + - run: apt install -y zlib1g-dev libffi-dev build-essential diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index 3b312a136..d1b5005d7 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -100,3 +100,8 @@ default-native = [ "native", "default-engine" ] + +# experimental / in-development features +experimental-reference-types-extern-ref = [ + "wasmer-types/experimental-reference-types-extern-ref", +] diff --git a/lib/api/src/externals/function.rs b/lib/api/src/externals/function.rs index a396c1ca2..0980d88ba 100644 --- a/lib/api/src/externals/function.rs +++ b/lib/api/src/externals/function.rs @@ -1,7 +1,7 @@ use crate::exports::{ExportError, Exportable}; use crate::externals::Extern; use crate::store::Store; -use crate::types::Val; +use crate::types::{Val, ValFuncRef}; use crate::FunctionType; use crate::NativeFunc; use crate::RuntimeError; @@ -18,7 +18,7 @@ use std::sync::Arc; use wasmer_engine::{Export, ExportFunction, ExportFunctionMetadata}; use wasmer_vm::{ raise_user_trap, resume_panic, wasmer_call_trampoline, ImportInitializerFuncPtr, - VMCallerCheckedAnyfunc, VMDynamicFunctionContext, VMExportFunction, VMFunctionBody, + VMCallerCheckedAnyfunc, VMDynamicFunctionContext, VMExportFunction, VMFuncRef, VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, VMTrampoline, }; @@ -70,6 +70,30 @@ pub struct Function { pub(crate) exported: ExportFunction, } +impl wasmer_types::WasmValueType for Function { + /// Write the value. + unsafe fn write_value_to(&self, p: *mut i128) { + let func_ref = + Val::into_vm_funcref(&Val::FuncRef(Some(self.clone())), &self.store).unwrap(); + std::ptr::write(p as *mut VMFuncRef, func_ref); + } + + /// Read the value. + // TODO(reftypes): this entire function should be cleaned up, `dyn Any` should + // ideally be removed + unsafe fn read_value_from(store: &dyn std::any::Any, p: *const i128) -> Self { + let func_ref = std::ptr::read(p as *const VMFuncRef); + let store = store.downcast_ref::().expect("Store expected in `Function::read_value_from`. If you see this error message it likely means you're using a function ref in a place we don't yet support it -- sorry about the inconvenience."); + match Val::from_vm_funcref(func_ref, store) { + Val::FuncRef(Some(fr)) => fr, + // these bottom two cases indicate bugs in `wasmer-types` or elsewhere. + // They should never be triggered, so we just panic. + Val::FuncRef(None) => panic!("Null funcref found in `Function::read_value_from`!"), + other => panic!("Invalid value in `Function::read_value_from`: {:?}", other), + } + } +} + fn build_export_function_metadata( env: Env, import_init_function_ptr: for<'a> fn( @@ -154,6 +178,7 @@ impl Function { let dynamic_ctx: VMDynamicFunctionContext = VMDynamicFunctionContext::from_context(DynamicFunctionWithoutEnv { func: Arc::new(func), + store: store.clone(), function_type: ty.clone(), }); // We don't yet have the address with the Wasm ABI signature. @@ -261,6 +286,7 @@ impl Function { VMDynamicFunctionContext::from_context(DynamicFunctionWithEnv { env: Box::new(env), func: Arc::new(func), + store: store.clone(), function_type: ty.clone(), }); @@ -546,7 +572,7 @@ impl Function { for (index, &value_type) in signature.results().iter().enumerate() { unsafe { let ptr = values_vec.as_ptr().add(index); - results[index] = Val::read_value_from(ptr, value_type); + results[index] = Val::read_value_from(&self.store, ptr, value_type); } } @@ -629,6 +655,7 @@ impl Function { FunctionDefinition::Wasm(wasm) => { self.call_wasm(&wasm, params, &mut results)?; } + // TODO: we can trivially hit this, look into it _ => unimplemented!("The function definition isn't supported for the moment"), } @@ -653,16 +680,14 @@ impl Function { } } - pub(crate) fn checked_anyfunc(&self) -> VMCallerCheckedAnyfunc { - let vmsignature = self - .store - .engine() - .register_signature(&self.exported.vm_function.signature); - VMCallerCheckedAnyfunc { + pub(crate) fn vm_funcref(&self) -> VMFuncRef { + let engine = self.store.engine(); + let vmsignature = engine.register_signature(&self.exported.vm_function.signature); + engine.register_function_metadata(VMCallerCheckedAnyfunc { func_ptr: self.exported.vm_function.address, type_index: vmsignature, vmctx: self.exported.vm_function.vmctx, - } + }) } /// Transform this WebAssembly function into a function with the @@ -812,6 +837,7 @@ impl fmt::Debug for Function { pub(crate) trait VMDynamicFunction: Send + Sync { fn call(&self, args: &[Val]) -> Result, RuntimeError>; fn function_type(&self) -> &FunctionType; + fn store(&self) -> &Store; } #[derive(Clone)] @@ -819,6 +845,7 @@ pub(crate) struct DynamicFunctionWithoutEnv { #[allow(clippy::type_complexity)] func: Arc Result, RuntimeError> + 'static + Send + Sync>, function_type: FunctionType, + store: Store, } impl VMDynamicFunction for DynamicFunctionWithoutEnv { @@ -828,6 +855,9 @@ impl VMDynamicFunction for DynamicFunctionWithoutEnv { fn function_type(&self) -> &FunctionType { &self.function_type } + fn store(&self) -> &Store { + &self.store + } } pub(crate) struct DynamicFunctionWithEnv @@ -837,6 +867,7 @@ where function_type: FunctionType, #[allow(clippy::type_complexity)] func: Arc Result, RuntimeError> + 'static + Send + Sync>, + store: Store, env: Box, } @@ -845,6 +876,7 @@ impl Clone for DynamicFunctionWithEn Self { env: self.env.clone(), function_type: self.function_type.clone(), + store: self.store.clone(), func: self.func.clone(), } } @@ -860,6 +892,9 @@ where fn function_type(&self) -> &FunctionType { &self.function_type } + fn store(&self) -> &Store { + &self.store + } } trait VMDynamicFunctionCall { @@ -892,8 +927,9 @@ impl VMDynamicFunctionCall for VMDynamicFunctionContext let result = panic::catch_unwind(AssertUnwindSafe(|| { let func_ty = self.ctx.function_type(); let mut args = Vec::with_capacity(func_ty.params().len()); + let store = self.ctx.store(); for (i, ty) in func_ty.params().iter().enumerate() { - args.push(Val::read_value_from(values_vec.add(i), *ty)); + args.push(Val::read_value_from(store, values_vec.add(i), *ty)); } let returns = self.ctx.call(&args)?; @@ -911,7 +947,9 @@ impl VMDynamicFunctionCall for VMDynamicFunctionContext ret.write_value_to(values_vec.add(i)); } Ok(()) - })); + })); // We get extern ref drops at the end of this block that we don't need. + // By preventing extern ref incs in the code above we can save the work of + // incrementing and decrementing. However the logic as-is is correct. match result { Ok(Ok(())) => {} @@ -929,6 +967,9 @@ mod inner { use std::error::Error; use std::marker::PhantomData; use std::panic::{self, AssertUnwindSafe}; + + #[cfg(feature = "experimental-reference-types-extern-ref")] + pub use wasmer_types::{ExternRef, VMExternRef}; use wasmer_types::{FunctionType, NativeWasmType, Type}; use wasmer_vm::{raise_user_trap, resume_panic, VMFunctionBody}; @@ -939,7 +980,7 @@ mod inner { /// `FromNativeWasmType` and `ToNativeWasmType` but it creates a /// non-negligible complexity in the `WasmTypeList` /// implementation. - pub unsafe trait FromToNativeWasmType: Copy + pub unsafe trait FromToNativeWasmType where Self: Sized, { @@ -1021,6 +1062,18 @@ mod inner { f64 => f64 ); + #[cfg(feature = "experimental-reference-types-extern-ref")] + unsafe impl FromToNativeWasmType for ExternRef { + type Native = VMExternRef; + + fn to_native(self) -> Self::Native { + self.into() + } + fn from_native(n: Self::Native) -> Self { + n.into() + } + } + #[cfg(test)] mod test_from_to_native_wasm_type { use super::*; diff --git a/lib/api/src/externals/global.rs b/lib/api/src/externals/global.rs index 4f85aba5d..0d111e0ee 100644 --- a/lib/api/src/externals/global.rs +++ b/lib/api/src/externals/global.rs @@ -126,7 +126,7 @@ impl Global { /// assert_eq!(g.get(), Value::I32(1)); /// ``` pub fn get(&self) -> Val { - self.global.get() + self.global.get(&self.store) } /// Sets a custom value [`Val`] to the runtime Global. diff --git a/lib/api/src/externals/table.rs b/lib/api/src/externals/table.rs index 6423c6cd9..6806067d2 100644 --- a/lib/api/src/externals/table.rs +++ b/lib/api/src/externals/table.rs @@ -7,7 +7,7 @@ use crate::TableType; use loupe::MemoryUsage; use std::sync::Arc; use wasmer_engine::{Export, ExportTable}; -use wasmer_vm::{Table as RuntimeTable, VMCallerCheckedAnyfunc, VMExportTable}; +use wasmer_vm::{Table as RuntimeTable, TableElement, VMExportTable}; /// A WebAssembly `table` instance. /// @@ -27,7 +27,7 @@ pub struct Table { fn set_table_item( table: &dyn RuntimeTable, item_index: u32, - item: VMCallerCheckedAnyfunc, + item: TableElement, ) -> Result<(), RuntimeError> { table.set(item_index, item).map_err(|e| e.into()) } @@ -40,7 +40,7 @@ impl Table { /// This function will construct the `Table` using the store /// [`BaseTunables`][crate::tunables::BaseTunables]. pub fn new(store: &Store, ty: TableType, init: Val) -> Result { - let item = init.into_checked_anyfunc(store)?; + let item = init.into_table_reference(store)?; let tunables = store.tunables(); let style = tunables.table_style(&ty); let table = tunables @@ -71,12 +71,12 @@ impl Table { /// Retrieves an element of the table at the provided `index`. pub fn get(&self, index: u32) -> Option { let item = self.table.get(index)?; - Some(ValFuncRef::from_checked_anyfunc(item, &self.store)) + Some(ValFuncRef::from_table_reference(item, &self.store)) } /// Sets an element `val` in the Table at the provided `index`. pub fn set(&self, index: u32, val: Val) -> Result<(), RuntimeError> { - let item = val.into_checked_anyfunc(&self.store)?; + let item = val.into_table_reference(&self.store)?; set_table_item(self.table.as_ref(), index, item) } @@ -95,19 +95,10 @@ impl Table { /// /// Returns an error if the `delta` is out of bounds for the table. pub fn grow(&self, delta: u32, init: Val) -> Result { - let item = init.into_checked_anyfunc(&self.store)?; - match self.table.grow(delta) { - Some(len) => { - for i in 0..delta { - set_table_item(self.table.as_ref(), len + i, item.clone())?; - } - Ok(len) - } - None => Err(RuntimeError::new(format!( - "failed to grow table by `{}`", - delta - ))), - } + let item = init.into_table_reference(&self.store)?; + self.table + .grow(delta, item) + .ok_or_else(|| RuntimeError::new(format!("failed to grow table by `{}`", delta))) } /// Copies the `len` elements of `src_table` starting at `src_index` diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index b299d5bcf..4c9508b1c 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -296,8 +296,8 @@ pub use crate::ptr::{Array, Item, WasmPtr}; pub use crate::store::{Store, StoreObject}; pub use crate::tunables::BaseTunables; pub use crate::types::{ - ExportType, ExternRef, ExternType, FunctionType, GlobalType, HostInfo, HostRef, ImportType, - MemoryType, Mutability, TableType, Val, ValType, + ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, + TableType, Val, ValType, }; pub use crate::types::{Val as Value, ValType as Type}; pub use crate::utils::is_wasm; @@ -314,6 +314,8 @@ pub use wasmer_engine::{ ChainableNamedResolver, DeserializeError, Engine, Export, FrameInfo, LinkError, NamedResolver, NamedResolverChain, Resolver, RuntimeError, SerializeError, Tunables, }; +#[cfg(feature = "experimental-reference-types-extern-ref")] +pub use wasmer_types::ExternRef; pub use wasmer_types::{ Atomically, Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, MemoryView, Pages, ValueType, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, diff --git a/lib/api/src/types.rs b/lib/api/src/types.rs index eeebbc34e..5a0ab3db6 100644 --- a/lib/api/src/types.rs +++ b/lib/api/src/types.rs @@ -1,12 +1,12 @@ use crate::externals::Function; use crate::store::{Store, StoreObject}; use crate::RuntimeError; -use std::ptr; use wasmer_types::Value; pub use wasmer_types::{ - ExportType, ExternRef, ExternType, FunctionType, GlobalType, HostInfo, HostRef, ImportType, - MemoryType, Mutability, TableType, Type as ValType, + ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, Mutability, + TableType, Type as ValType, }; +use wasmer_vm::VMFuncRef; /// WebAssembly computations manipulate values of basic value types: /// * Integers (32 or 64 bit width) @@ -19,9 +19,10 @@ pub type Val = Value; impl StoreObject for Val { fn comes_from_same_store(&self, store: &Store) -> bool { match self { - Self::FuncRef(f) => Store::same(store, f.store()), - Self::ExternRef(ExternRef::Ref(_)) | Self::ExternRef(ExternRef::Other(_)) => false, - Self::ExternRef(ExternRef::Null) => true, + Self::FuncRef(None) => true, + Self::FuncRef(Some(f)) => Store::same(store, f.store()), + // `ExternRef`s are not tied to specific stores + Self::ExternRef(_) => true, Self::I32(_) | Self::I64(_) | Self::F32(_) | Self::F64(_) | Self::V128(_) => true, } } @@ -29,46 +30,42 @@ impl StoreObject for Val { impl From for Val { fn from(val: Function) -> Self { - Self::FuncRef(val) + Self::FuncRef(Some(val)) } } /// It provides useful functions for converting back and forth /// from [`Val`] into `FuncRef`. pub trait ValFuncRef { - fn into_checked_anyfunc( - &self, - store: &Store, - ) -> Result; + fn into_vm_funcref(&self, store: &Store) -> Result; - fn from_checked_anyfunc(item: wasmer_vm::VMCallerCheckedAnyfunc, store: &Store) -> Self; + fn from_vm_funcref(item: VMFuncRef, store: &Store) -> Self; + + fn into_table_reference(&self, store: &Store) -> Result; + + fn from_table_reference(item: wasmer_vm::TableElement, store: &Store) -> Self; } impl ValFuncRef for Val { - fn into_checked_anyfunc( - &self, - store: &Store, - ) -> Result { + fn into_vm_funcref(&self, store: &Store) -> Result { if !self.comes_from_same_store(store) { return Err(RuntimeError::new("cross-`Store` values are not supported")); } Ok(match self { - Self::ExternRef(ExternRef::Null) => wasmer_vm::VMCallerCheckedAnyfunc { - func_ptr: ptr::null(), - type_index: wasmer_vm::VMSharedSignatureIndex::default(), - vmctx: wasmer_vm::VMFunctionEnvironment { - host_env: ptr::null_mut(), - }, - }, - Self::FuncRef(f) => f.checked_anyfunc(), - _ => return Err(RuntimeError::new("val is not funcref")), + Self::FuncRef(None) => VMFuncRef::null(), + Self::FuncRef(Some(f)) => f.vm_funcref(), + _ => return Err(RuntimeError::new("val is not func ref")), }) } - fn from_checked_anyfunc(item: wasmer_vm::VMCallerCheckedAnyfunc, store: &Store) -> Self { - if item.type_index == wasmer_vm::VMSharedSignatureIndex::default() { - return Self::ExternRef(ExternRef::Null); + fn from_vm_funcref(func_ref: VMFuncRef, store: &Store) -> Self { + if func_ref.is_null() { + return Self::FuncRef(None); } + let item: &wasmer_vm::VMCallerCheckedAnyfunc = unsafe { + let anyfunc: *const wasmer_vm::VMCallerCheckedAnyfunc = *func_ref; + &*anyfunc + }; let signature = store .engine() .lookup_signature(item.type_index) @@ -80,6 +77,7 @@ impl ValFuncRef for Val { vm_function: wasmer_vm::VMExportFunction { address: item.func_ptr, signature, + // TODO: review this comment (unclear if it's still correct): // All functions in tables are already Static (as dynamic functions // are converted to use the trampolines with static signatures). kind: wasmer_vm::VMFunctionKind::Static, @@ -89,6 +87,28 @@ impl ValFuncRef for Val { }, }; let f = Function::from_vm_export(store, export); - Self::FuncRef(f) + Self::FuncRef(Some(f)) + } + + fn into_table_reference(&self, store: &Store) -> Result { + if !self.comes_from_same_store(store) { + return Err(RuntimeError::new("cross-`Store` values are not supported")); + } + Ok(match self { + // TODO(reftypes): review this clone + Self::ExternRef(extern_ref) => { + wasmer_vm::TableElement::ExternRef(extern_ref.clone().into()) + } + Self::FuncRef(None) => wasmer_vm::TableElement::FuncRef(VMFuncRef::null()), + Self::FuncRef(Some(f)) => wasmer_vm::TableElement::FuncRef(f.vm_funcref()), + _ => return Err(RuntimeError::new("val is not reference")), + }) + } + + fn from_table_reference(item: wasmer_vm::TableElement, store: &Store) -> Self { + match item { + wasmer_vm::TableElement::FuncRef(f) => Self::from_vm_funcref(f, store), + wasmer_vm::TableElement::ExternRef(extern_ref) => Self::ExternRef(extern_ref.into()), + } } } diff --git a/lib/api/tests/externals.rs b/lib/api/tests/externals.rs index 5dec277bc..767272e87 100644 --- a/lib/api/tests/externals.rs +++ b/lib/api/tests/externals.rs @@ -67,7 +67,7 @@ fn table_new() -> Result<()> { maximum: None, }; let f = Function::new_native(&store, || {}); - let table = Table::new(&store, table_type, Value::FuncRef(f))?; + let table = Table::new(&store, table_type, Value::FuncRef(Some(f)))?; assert_eq!(*table.ty(), table_type); // Anyrefs not yet supported @@ -92,7 +92,7 @@ fn table_get() -> Result<()> { maximum: Some(1), }; let f = Function::new_native(&store, |num: i32| num + 1); - let table = Table::new(&store, table_type, Value::FuncRef(f.clone()))?; + let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?; assert_eq!(*table.ty(), table_type); let _elem = table.get(0).unwrap(); // assert_eq!(elem.funcref().unwrap(), f); @@ -115,13 +115,13 @@ fn table_grow() -> Result<()> { maximum: Some(10), }; let f = Function::new_native(&store, |num: i32| num + 1); - let table = Table::new(&store, table_type, Value::FuncRef(f.clone()))?; + let table = Table::new(&store, table_type, Value::FuncRef(Some(f.clone())))?; // Growing to a bigger maximum should return None - let old_len = table.grow(12, Value::FuncRef(f.clone())); + let old_len = table.grow(12, Value::FuncRef(Some(f.clone()))); assert!(old_len.is_err()); // Growing to a bigger maximum should return None - let old_len = table.grow(5, Value::FuncRef(f.clone()))?; + let old_len = table.grow(5, Value::FuncRef(Some(f.clone())))?; assert_eq!(old_len, 0); Ok(()) diff --git a/lib/api/tests/reference_types.rs b/lib/api/tests/reference_types.rs new file mode 100644 index 000000000..8a1aaa6e0 --- /dev/null +++ b/lib/api/tests/reference_types.rs @@ -0,0 +1,497 @@ +use anyhow::Result; +use std::collections::HashMap; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use wasmer::*; + +#[test] +fn func_ref_passed_and_returned() -> Result<()> { + let store = Store::default(); + let wat = r#"(module + (import "env" "func_ref_identity" (func (param funcref) (result funcref))) + (type $ret_i32_ty (func (result i32))) + (table $table (export "table") 2 2 funcref) + + (func (export "run") (param) (result funcref) + (call 0 (ref.null func))) + (func (export "call_set_value") (param $fr funcref) (result i32) + (table.set $table (i32.const 0) (local.get $fr)) + (call_indirect $table (type $ret_i32_ty) (i32.const 0))) +)"#; + let module = Module::new(&store, wat)?; + let imports = imports! { + "env" => { + "func_ref_identity" => Function::new(&store, FunctionType::new([Type::FuncRef], [Type::FuncRef]), |values| -> Result, _> { + Ok(vec![values[0].clone()]) + }) + }, + }; + + let instance = Instance::new(&module, &imports)?; + + let f: &Function = instance.exports.get_function("run")?; + let results = f.call(&[]).unwrap(); + if let Value::FuncRef(fr) = &results[0] { + assert!(fr.is_none()); + } else { + panic!("funcref not found!"); + } + + #[derive(Clone, Debug, WasmerEnv)] + pub struct Env(Arc); + let env = Env(Arc::new(AtomicBool::new(false))); + + let func_to_call = Function::new_native_with_env(&store, env.clone(), |env: &Env| -> i32 { + env.0.store(true, Ordering::SeqCst); + 343 + }); + let call_set_value: &Function = instance.exports.get_function("call_set_value")?; + let results: Box<[Value]> = call_set_value.call(&[Value::FuncRef(Some(func_to_call))])?; + assert!(env.0.load(Ordering::SeqCst)); + assert_eq!(&*results, &[Value::I32(343)]); + + Ok(()) +} + +#[test] +fn func_ref_passed_and_called() -> Result<()> { + let store = Store::default(); + let wat = r#"(module + (func $func_ref_call (import "env" "func_ref_call") (param funcref) (result i32)) + (type $ret_i32_ty (func (result i32))) + (table $table (export "table") 2 2 funcref) + + (func $product (param $x i32) (param $y i32) (result i32) + (i32.mul (local.get $x) (local.get $y))) + ;; TODO: figure out exactly why this statement is needed + (elem declare func $product) + (func (export "call_set_value") (param $fr funcref) (result i32) + (table.set $table (i32.const 0) (local.get $fr)) + (call_indirect $table (type $ret_i32_ty) (i32.const 0))) + (func (export "call_func") (param $fr funcref) (result i32) + (call $func_ref_call (local.get $fr))) + (func (export "call_host_func_with_wasm_func") (result i32) + (call $func_ref_call (ref.func $product))) +)"#; + let module = Module::new(&store, wat)?; + + fn func_ref_call(values: &[Value]) -> Result, RuntimeError> { + // TODO: look into `Box<[Value]>` being returned breakage + let f = values[0].unwrap_funcref().as_ref().unwrap(); + let f: NativeFunc<(i32, i32), i32> = f.native()?; + Ok(vec![Value::I32(f.call(7, 9)?)]) + } + + let imports = imports! { + "env" => { + "func_ref_call" => Function::new( + &store, + FunctionType::new([Type::FuncRef], [Type::I32]), + func_ref_call + ), + // TODO(reftypes): this should work + /* + "func_ref_call_native" => Function::new_native(&store, |f: Function| -> Result { + let f: NativeFunc::<(i32, i32), i32> = f.native()?; + f.call(7, 9) + }) + */ + }, + }; + + let instance = Instance::new(&module, &imports)?; + { + fn sum(a: i32, b: i32) -> i32 { + a + b + } + let sum_func = Function::new_native(&store, sum); + + let call_func: &Function = instance.exports.get_function("call_func")?; + let result = call_func.call(&[Value::FuncRef(Some(sum_func))])?; + assert_eq!(result[0].unwrap_i32(), 16); + } + + { + let f: NativeFunc<(), i32> = instance + .exports + .get_native_function("call_host_func_with_wasm_func")?; + let result = f.call()?; + assert_eq!(result, 63); + } + + Ok(()) +} + +#[cfg(feature = "experimental-reference-types-extern-ref")] +#[test] +fn extern_ref_passed_and_returned() -> Result<()> { + let store = Store::default(); + let wat = r#"(module + (func $extern_ref_identity (import "env" "extern_ref_identity") (param externref) (result externref)) + (func $extern_ref_identity_native (import "env" "extern_ref_identity_native") (param externref) (result externref)) + (func $get_new_extern_ref (import "env" "get_new_extern_ref") (result externref)) + (func $get_new_extern_ref_native (import "env" "get_new_extern_ref_native") (result externref)) + + (func (export "run") (param) (result externref) + (call $extern_ref_identity (ref.null extern))) + (func (export "run_native") (param) (result externref) + (call $extern_ref_identity_native (ref.null extern))) + (func (export "get_hashmap") (param) (result externref) + (call $get_new_extern_ref)) + (func (export "get_hashmap_native") (param) (result externref) + (call $get_new_extern_ref_native)) +)"#; + let module = Module::new(&store, wat)?; + let imports = imports! { + "env" => { + "extern_ref_identity" => Function::new(&store, FunctionType::new([Type::ExternRef], [Type::ExternRef]), |values| -> Result, _> { + Ok(vec![values[0].clone()]) + }), + "extern_ref_identity_native" => Function::new_native(&store, |er: ExternRef| -> ExternRef { + er + }), + "get_new_extern_ref" => Function::new(&store, FunctionType::new([], [Type::ExternRef]), |_| -> Result, _> { + let inner = + [("hello".to_string(), "world".to_string()), + ("color".to_string(), "orange".to_string())] + .iter() + .cloned() + .collect::>(); + let new_extern_ref = ExternRef::new(inner); + Ok(vec![Value::ExternRef(new_extern_ref)]) + }), + "get_new_extern_ref_native" => Function::new_native(&store, || -> ExternRef { + let inner = + [("hello".to_string(), "world".to_string()), + ("color".to_string(), "orange".to_string())] + .iter() + .cloned() + .collect::>(); + ExternRef::new(inner) + }) + }, + }; + + let instance = Instance::new(&module, &imports)?; + for run in &["run", "run_native"] { + let f: &Function = instance.exports.get_function(run)?; + let results = f.call(&[]).unwrap(); + if let Value::ExternRef(er) = &results[0] { + assert!(er.is_null()); + } else { + panic!("result is not an extern ref!"); + } + + let f: NativeFunc<(), ExternRef> = instance.exports.get_native_function(run)?; + let result: ExternRef = f.call()?; + assert!(result.is_null()); + } + + for get_hashmap in &["get_hashmap", "get_hashmap_native"] { + let f: &Function = instance.exports.get_function(get_hashmap)?; + let results = f.call(&[]).unwrap(); + if let Value::ExternRef(er) = &results[0] { + let inner: &HashMap = er.downcast().unwrap(); + assert_eq!(inner["hello"], "world"); + assert_eq!(inner["color"], "orange"); + } else { + panic!("result is not an extern ref!"); + } + + let f: NativeFunc<(), ExternRef> = instance.exports.get_native_function(get_hashmap)?; + + let result: ExternRef = f.call()?; + let inner: &HashMap = result.downcast().unwrap(); + assert_eq!(inner["hello"], "world"); + assert_eq!(inner["color"], "orange"); + } + + Ok(()) +} + +#[cfg(feature = "experimental-reference-types-extern-ref")] +#[test] +// TODO(reftypes): reenable this test +#[ignore] +fn extern_ref_ref_counting_basic() -> Result<()> { + let store = Store::default(); + let wat = r#"(module + (func (export "drop") (param $er externref) (result) + (drop (local.get $er))) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&module, &imports! {})?; + let f: NativeFunc = instance.exports.get_native_function("drop")?; + + let er = ExternRef::new(3u32); + f.call(er.clone())?; + + assert_eq!(er.downcast::().unwrap(), &3); + assert_eq!(er.strong_count(), 1); + + Ok(()) +} + +#[cfg(feature = "experimental-reference-types-extern-ref")] +#[test] +fn refs_in_globals() -> Result<()> { + let store = Store::default(); + let wat = r#"(module + (global $er_global (export "er_global") (mut externref) (ref.null extern)) + (global $fr_global (export "fr_global") (mut funcref) (ref.null func)) + (global $fr_immutable_global (export "fr_immutable_global") funcref (ref.func $hello)) + (func $hello (param) (result i32) + (i32.const 73)) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&module, &imports! {})?; + { + let er_global: &Global = instance.exports.get_global("er_global")?; + + if let Value::ExternRef(er) = er_global.get() { + assert!(er.is_null()); + } else { + panic!("Did not find extern ref in the global"); + } + + er_global.set(Val::ExternRef(ExternRef::new(3u32)))?; + + if let Value::ExternRef(er) = er_global.get() { + assert_eq!(er.downcast::().unwrap(), &3); + assert_eq!(er.strong_count(), 1); + } else { + panic!("Did not find extern ref in the global"); + } + } + + { + let fr_global: &Global = instance.exports.get_global("fr_immutable_global")?; + + if let Value::FuncRef(Some(f)) = fr_global.get() { + let native_func: NativeFunc<(), u32> = f.native()?; + assert_eq!(native_func.call()?, 73); + } else { + panic!("Did not find non-null func ref in the global"); + } + } + + { + let fr_global: &Global = instance.exports.get_global("fr_global")?; + + if let Value::FuncRef(None) = fr_global.get() { + } else { + panic!("Did not find a null func ref in the global"); + } + + let f = Function::new_native(&store, |arg1: i32, arg2: i32| -> i32 { arg1 + arg2 }); + + fr_global.set(Val::FuncRef(Some(f)))?; + + if let Value::FuncRef(Some(f)) = fr_global.get() { + let native: NativeFunc<(i32, i32), i32> = f.native()?; + assert_eq!(native.call(5, 7)?, 12); + } else { + panic!("Did not find extern ref in the global"); + } + } + + Ok(()) +} + +#[cfg(feature = "experimental-reference-types-extern-ref")] +#[test] +fn extern_ref_ref_counting_table_basic() -> Result<()> { + let store = Store::default(); + let wat = r#"(module + (global $global (export "global") (mut externref) (ref.null extern)) + (table $table (export "table") 4 4 externref) + (func $insert (param $er externref) (param $idx i32) + (table.set $table (local.get $idx) (local.get $er))) + (func $intermediate (param $er externref) (param $idx i32) + (call $insert (local.get $er) (local.get $idx))) + (func $insert_into_table (export "insert_into_table") (param $er externref) (param $idx i32) (result externref) + (call $intermediate (local.get $er) (local.get $idx)) + (local.get $er)) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&module, &imports! {})?; + + let f: NativeFunc<(ExternRef, i32), ExternRef> = + instance.exports.get_native_function("insert_into_table")?; + + let er = ExternRef::new(3usize); + + let er = f.call(er, 1)?; + assert_eq!(er.strong_count(), 2); + + let table: &Table = instance.exports.get_table("table")?; + + { + let er2 = table.get(1).unwrap().externref().unwrap(); + assert_eq!(er2.strong_count(), 3); + } + + assert_eq!(er.strong_count(), 2); + table.set(1, Val::ExternRef(ExternRef::null()))?; + + assert_eq!(er.strong_count(), 1); + + Ok(()) +} + +#[cfg(feature = "experimental-reference-types-extern-ref")] +#[test] +// TODO(reftypes): reenable this test +#[ignore] +fn extern_ref_ref_counting_global_basic() -> Result<()> { + let store = Store::default(); + let wat = r#"(module + (global $global (export "global") (mut externref) (ref.null extern)) + (func $get_from_global (export "get_from_global") (result externref) + (drop (global.get $global)) + (global.get $global)) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&module, &imports! {})?; + + let global: &Global = instance.exports.get_global("global")?; + { + let er = ExternRef::new(3usize); + global.set(Val::ExternRef(er.clone()))?; + assert_eq!(er.strong_count(), 2); + } + let get_from_global: NativeFunc<(), ExternRef> = + instance.exports.get_native_function("get_from_global")?; + + let er = get_from_global.call()?; + assert_eq!(er.strong_count(), 2); + global.set(Val::ExternRef(ExternRef::null()))?; + assert_eq!(er.strong_count(), 1); + + Ok(()) +} + +#[cfg(feature = "experimental-reference-types-extern-ref")] +#[test] +// TODO(reftypes): reenable this test +#[ignore] +fn extern_ref_ref_counting_traps() -> Result<()> { + let store = Store::default(); + let wat = r#"(module + (func $pass_er (export "pass_extern_ref") (param externref) + (local.get 0) + (unreachable)) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&module, &imports! {})?; + + let pass_extern_ref: NativeFunc = + instance.exports.get_native_function("pass_extern_ref")?; + + let er = ExternRef::new(3usize); + assert_eq!(er.strong_count(), 1); + + let result = pass_extern_ref.call(er.clone()); + assert!(result.is_err()); + assert_eq!(er.strong_count(), 1); + + Ok(()) +} + +#[cfg(feature = "experimental-reference-types-extern-ref")] +#[test] +fn extern_ref_ref_counting_table_instructions() -> Result<()> { + let store = Store::default(); + let wat = r#"(module + (table $table1 (export "table1") 2 12 externref) + (table $table2 (export "table2") 6 12 externref) + (func $grow_table_with_ref (export "grow_table_with_ref") (param $er externref) (param $size i32) (result i32) + (table.grow $table1 (local.get $er) (local.get $size))) + (func $fill_table_with_ref (export "fill_table_with_ref") (param $er externref) (param $start i32) (param $end i32) + (table.fill $table1 (local.get $start) (local.get $er) (local.get $end))) + (func $copy_into_table2 (export "copy_into_table2") + (table.copy $table2 $table1 (i32.const 0) (i32.const 0) (i32.const 4))) +)"#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&module, &imports! {})?; + + let grow_table_with_ref: NativeFunc<(ExternRef, i32), i32> = instance + .exports + .get_native_function("grow_table_with_ref")?; + let fill_table_with_ref: NativeFunc<(ExternRef, i32, i32), ()> = instance + .exports + .get_native_function("fill_table_with_ref")?; + let copy_into_table2: NativeFunc<(), ()> = + instance.exports.get_native_function("copy_into_table2")?; + let table1: &Table = instance.exports.get_table("table1")?; + let table2: &Table = instance.exports.get_table("table2")?; + + let er1 = ExternRef::new(3usize); + let er2 = ExternRef::new(5usize); + let er3 = ExternRef::new(7usize); + { + let result = grow_table_with_ref.call(er1.clone(), 0)?; + assert_eq!(result, 2); + assert_eq!(er1.strong_count(), 1); + + let result = grow_table_with_ref.call(er1.clone(), 10_000)?; + assert_eq!(result, -1); + assert_eq!(er1.strong_count(), 1); + + let result = grow_table_with_ref.call(er1.clone(), 8)?; + assert_eq!(result, 2); + assert_eq!(er1.strong_count(), 9); + + for i in 2..10 { + let e = table1.get(i).unwrap().unwrap_externref(); + assert_eq!(*e.downcast::().unwrap(), 3); + assert_eq!(&e, &er1); + } + assert_eq!(er1.strong_count(), 9); + } + + { + fill_table_with_ref.call(er2.clone(), 0, 2)?; + assert_eq!(er2.strong_count(), 3); + } + + { + table2.set(0, Val::ExternRef(er3.clone()))?; + table2.set(1, Val::ExternRef(er3.clone()))?; + table2.set(2, Val::ExternRef(er3.clone()))?; + table2.set(3, Val::ExternRef(er3.clone()))?; + table2.set(4, Val::ExternRef(er3.clone()))?; + assert_eq!(er3.strong_count(), 6); + } + + { + copy_into_table2.call()?; + assert_eq!(er3.strong_count(), 2); + assert_eq!(er2.strong_count(), 5); + assert_eq!(er1.strong_count(), 11); + for i in 1..5 { + let e = table2.get(i).unwrap().unwrap_externref(); + let value = e.downcast::().unwrap(); + match i { + 0 | 1 => assert_eq!(*value, 5), + 4 => assert_eq!(*value, 7), + _ => assert_eq!(*value, 3), + } + } + } + + { + for i in 0..table1.size() { + table1.set(i, Val::ExternRef(ExternRef::null()))?; + } + for i in 0..table2.size() { + table2.set(i, Val::ExternRef(ExternRef::null()))?; + } + } + + assert_eq!(er1.strong_count(), 1); + assert_eq!(er2.strong_count(), 1); + assert_eq!(er3.strong_count(), 1); + + Ok(()) +} diff --git a/lib/c-api/CHANGELOG.md b/lib/c-api/CHANGELOG.md new file mode 100644 index 000000000..aa9c4dac2 --- /dev/null +++ b/lib/c-api/CHANGELOG.md @@ -0,0 +1,22 @@ +# C API Changelog + +*The format is based on [Keep a Changelog].* + +[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ + +Looking for changes to the Wasmer CLI and the Rust API? See our [Primary Changelog](../../CHANGELOG.md) + + +## **[Unreleased]** + +### Added +- [#2208](https://github.com/wasmerio/wasmer/pull/2208) Add a new CHANGELOG.md specific to our C API to make it easier for users primarily consuming our C API to keep up to date with changes that affect them. + +### Changed + +### Fixed +- [#2208](https://github.com/wasmerio/wasmer/pull/2208) Fix ownership in Wasm C API of `wasm_extern_as_func`, `wasm_extern_as_memory`, `wasm_extern_as_table`, `wasm_extern_as_global`, `wasm_func_as_extern`, `wasm_memory_as_extern`, `wasm_table_as_extern`, and `wasm_global_as_extern`. These functions no longer allocate memory and thus their results should not be freed. This is a breaking change to align more closely with the Wasm C API's stated ownership. + +## Changes before 2020-04-06 + +See the [Primary Changelog](../../CHANGELOG.md). diff --git a/lib/c-api/Cargo.toml b/lib/c-api/Cargo.toml index e8d559a4d..0bd8455c1 100644 --- a/lib/c-api/Cargo.toml +++ b/lib/c-api/Cargo.toml @@ -38,6 +38,7 @@ typetag = { version = "0.1", optional = true } paste = "1.0" [dev-dependencies] +field-offset = "0.3.3" inline-c = "0.1.5" [features] @@ -53,7 +54,7 @@ wat = ["wasmer/wat"] wasi = ["wasmer-wasi", "typetag", "serde"] engine = [] middlewares = ["wasmer-middlewares"] -deprecated = ["libffi"] +deprecated = ["libffi", "wasmer/experimental-reference-types-extern-ref"] jit = [ "wasmer-engine-jit", "engine", diff --git a/lib/c-api/examples/exports-function.c b/lib/c-api/examples/exports-function.c index e9c1653c0..e7a74f30d 100644 --- a/lib/c-api/examples/exports-function.c +++ b/lib/c-api/examples/exports-function.c @@ -77,7 +77,6 @@ int main(int argc, const char* argv[]) { printf("Results of `sum`: %d\n", results_val[0].of.i32); - wasm_func_delete(sum_func); wasm_module_delete(module); wasm_instance_delete(instance); wasm_extern_vec_delete(&exports); diff --git a/lib/c-api/examples/exports-global.c b/lib/c-api/examples/exports-global.c index 7673e39fe..ae576038d 100644 --- a/lib/c-api/examples/exports-global.c +++ b/lib/c-api/examples/exports-global.c @@ -109,8 +109,6 @@ int main(int argc, const char* argv[]) { wasm_global_set(some, &some_set_value); printf("`some` value: %.1f\n", some_value.of.f32); - wasm_global_delete(some); - wasm_global_delete(one); wasm_module_delete(module); wasm_extern_vec_delete(&exports); wasm_instance_delete(instance); diff --git a/lib/c-api/examples/imports-exports.c b/lib/c-api/examples/imports-exports.c index aad86104e..b233e647f 100644 --- a/lib/c-api/examples/imports-exports.c +++ b/lib/c-api/examples/imports-exports.c @@ -124,10 +124,6 @@ int main(int argc, const char* argv[]) { printf("Got the exported memory: %p\n", memory); - wasm_func_delete(func); - wasm_global_delete(global); - wasm_table_delete(table); - wasm_memory_delete(memory); wasm_module_delete(module); wasm_extern_vec_delete(&exports); wasm_instance_delete(instance); diff --git a/lib/c-api/examples/memory.c b/lib/c-api/examples/memory.c index 325a41cba..33e0e22fb 100644 --- a/lib/c-api/examples/memory.c +++ b/lib/c-api/examples/memory.c @@ -99,10 +99,6 @@ int main(int argc, const char* argv[]) { printf("Value at 0x%04x: %d\n", mem_addr, get_at_results_val[0].of.i32); - wasm_memory_delete(memory); - wasm_func_delete(mem_size); - wasm_func_delete(set_at); - wasm_func_delete(get_at); wasm_extern_vec_delete(&exports); wasm_module_delete(module); wasm_instance_delete(instance); diff --git a/lib/c-api/src/deprecated/table.rs b/lib/c-api/src/deprecated/table.rs index d6da3fce1..5043d4857 100644 --- a/lib/c-api/src/deprecated/table.rs +++ b/lib/c-api/src/deprecated/table.rs @@ -18,7 +18,7 @@ fn get_default_table_value(table_type: ValType) -> Val { ValType::F64 => Val::F64(0.), ValType::V128 => Val::V128(0), ValType::ExternRef => Val::ExternRef(ExternRef::null()), - ValType::FuncRef => Val::ExternRef(ExternRef::null()), + ValType::FuncRef => Val::FuncRef(None), } } diff --git a/lib/c-api/src/wasm_c_api/externals/function.rs b/lib/c-api/src/wasm_c_api/externals/function.rs index 8d1abdedd..98d184649 100644 --- a/lib/c-api/src/wasm_c_api/externals/function.rs +++ b/lib/c-api/src/wasm_c_api/externals/function.rs @@ -2,17 +2,27 @@ use super::super::store::wasm_store_t; use super::super::trap::wasm_trap_t; use super::super::types::{wasm_functype_t, wasm_valkind_enum}; use super::super::value::{wasm_val_inner, wasm_val_t, wasm_val_vec_t}; +use super::CApiExternTag; use std::convert::TryInto; use std::ffi::c_void; use std::sync::Arc; -use wasmer::{Function, Instance, RuntimeError, Val}; +use wasmer::{Function, RuntimeError, Val}; -#[derive(Debug)] +#[derive(Debug, Clone)] #[allow(non_camel_case_types)] +#[repr(C)] pub struct wasm_func_t { - pub(crate) inner: Function, - // this is how we ensure the instance stays alive - pub(crate) instance: Option>, + pub(crate) tag: CApiExternTag, + pub(crate) inner: Box, +} + +impl wasm_func_t { + pub(crate) fn new(function: Function) -> Self { + Self { + tag: CApiExternTag::Function, + inner: Box::new(function), + } + } } #[allow(non_camel_case_types)] @@ -80,10 +90,7 @@ pub unsafe extern "C" fn wasm_func_new( }; let function = Function::new(&store.inner, func_sig, inner_callback); - Some(Box::new(wasm_func_t { - instance: None, - inner: function, - })) + Some(Box::new(wasm_func_t::new(function))) } #[no_mangle] @@ -115,9 +122,8 @@ pub unsafe extern "C" fn wasm_func_new_with_env( impl Drop for WrapperEnv { fn drop(&mut self) { - if let Some(env_finalizer) = Arc::get_mut(&mut self.env_finalizer) - .map(Option::take) - .flatten() + if let Some(env_finalizer) = + Arc::get_mut(&mut self.env_finalizer).and_then(Option::take) { if !self.env.is_null() { unsafe { (env_finalizer)(self.env as _) } @@ -172,10 +178,7 @@ pub unsafe extern "C" fn wasm_func_new_with_env( trampoline, ); - Some(Box::new(wasm_func_t { - instance: None, - inner: function, - })) + Some(Box::new(wasm_func_t::new(function))) } #[no_mangle] diff --git a/lib/c-api/src/wasm_c_api/externals/global.rs b/lib/c-api/src/wasm_c_api/externals/global.rs index 624d4620f..14b6edb71 100644 --- a/lib/c-api/src/wasm_c_api/externals/global.rs +++ b/lib/c-api/src/wasm_c_api/externals/global.rs @@ -1,14 +1,26 @@ use super::super::store::wasm_store_t; use super::super::types::wasm_globaltype_t; use super::super::value::wasm_val_t; +use super::CApiExternTag; use crate::error::update_last_error; use std::convert::TryInto; use wasmer::{Global, Val}; #[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Clone, Debug)] pub struct wasm_global_t { - // maybe needs to hold onto instance - pub(crate) inner: Global, + pub(crate) tag: CApiExternTag, + pub(crate) inner: Box, +} + +impl wasm_global_t { + pub(crate) fn new(global: Global) -> Self { + Self { + tag: CApiExternTag::Global, + inner: Box::new(global), + } + } } #[no_mangle] @@ -30,7 +42,7 @@ pub unsafe extern "C" fn wasm_global_new( Global::new(store, wasm_val) }; - Some(Box::new(wasm_global_t { inner: global })) + Some(Box::new(wasm_global_t::new(global))) } #[no_mangle] @@ -40,9 +52,7 @@ pub unsafe extern "C" fn wasm_global_delete(_global: Option>) #[no_mangle] pub unsafe extern "C" fn wasm_global_copy(global: &wasm_global_t) -> Box { // do shallow copy - Box::new(wasm_global_t { - inner: global.inner.clone(), - }) + Box::new(wasm_global_t::new((&*global.inner).clone())) } #[no_mangle] diff --git a/lib/c-api/src/wasm_c_api/externals/memory.rs b/lib/c-api/src/wasm_c_api/externals/memory.rs index e43db12ee..4e73c6aec 100644 --- a/lib/c-api/src/wasm_c_api/externals/memory.rs +++ b/lib/c-api/src/wasm_c_api/externals/memory.rs @@ -1,12 +1,24 @@ use super::super::store::wasm_store_t; use super::super::types::wasm_memorytype_t; +use super::CApiExternTag; use std::mem; use wasmer::{Memory, Pages}; #[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Clone, Debug)] pub struct wasm_memory_t { - // maybe needs to hold onto instance - pub(crate) inner: Memory, + pub(crate) tag: CApiExternTag, + pub(crate) inner: Box, +} + +impl wasm_memory_t { + pub(crate) fn new(memory: Memory) -> Self { + Self { + tag: CApiExternTag::Memory, + inner: Box::new(memory), + } + } } #[no_mangle] @@ -20,7 +32,7 @@ pub unsafe extern "C" fn wasm_memory_new( let memory_type = memory_type.inner().memory_type.clone(); let memory = c_try!(Memory::new(&store.inner, memory_type)); - Some(Box::new(wasm_memory_t { inner: memory })) + Some(Box::new(wasm_memory_t::new(memory))) } #[no_mangle] @@ -30,9 +42,7 @@ pub unsafe extern "C" fn wasm_memory_delete(_memory: Option>) #[no_mangle] pub unsafe extern "C" fn wasm_memory_copy(memory: &wasm_memory_t) -> Box { // do shallow copy - Box::new(wasm_memory_t { - inner: memory.inner.clone(), - }) + Box::new(wasm_memory_t::new((&*memory.inner).clone())) } #[no_mangle] diff --git a/lib/c-api/src/wasm_c_api/externals/mod.rs b/lib/c-api/src/wasm_c_api/externals/mod.rs index 869ae4b6e..2b8a670b9 100644 --- a/lib/c-api/src/wasm_c_api/externals/mod.rs +++ b/lib/c-api/src/wasm_c_api/externals/mod.rs @@ -6,16 +6,167 @@ mod table; pub use function::*; pub use global::*; pub use memory::*; -use std::sync::Arc; +use std::mem; pub use table::*; -use wasmer::{Extern, Instance}; +use wasmer::{Extern, ExternType}; #[allow(non_camel_case_types)] -#[derive(Clone)] +#[repr(transparent)] pub struct wasm_extern_t { - // this is how we ensure the instance stays alive - pub(crate) instance: Option>, - pub(crate) inner: Extern, + pub(crate) inner: wasm_extern_inner, +} + +/// All elements in this union must be `repr(C)` and have a +/// `CApiExternTag` as their first element. +#[allow(non_camel_case_types)] +pub(crate) union wasm_extern_inner { + function: mem::ManuallyDrop, + memory: mem::ManuallyDrop, + global: mem::ManuallyDrop, + table: mem::ManuallyDrop, +} + +#[cfg(test)] +mod extern_tests { + use super::*; + + #[test] + fn externs_are_the_same_size() { + use std::mem::{align_of, size_of}; + assert_eq!(size_of::(), size_of::()); + assert_eq!(size_of::(), size_of::()); + assert_eq!(size_of::(), size_of::()); + assert_eq!(size_of::(), size_of::()); + + assert_eq!(align_of::(), align_of::()); + assert_eq!(align_of::(), align_of::()); + assert_eq!(align_of::(), align_of::()); + assert_eq!(align_of::(), align_of::()); + } + + #[test] + fn tags_are_the_same_offset_away() { + use field_offset::offset_of; + + let func_tag_offset = offset_of!(wasm_func_t => tag).get_byte_offset(); + let memory_tag_offset = offset_of!(wasm_memory_t => tag).get_byte_offset(); + let global_tag_offset = offset_of!(wasm_global_t => tag).get_byte_offset(); + let table_tag_offset = offset_of!(wasm_table_t => tag).get_byte_offset(); + + assert_eq!(func_tag_offset, memory_tag_offset); + assert_eq!(global_tag_offset, table_tag_offset); + assert_eq!(func_tag_offset, global_tag_offset); + } +} + +impl Drop for wasm_extern_inner { + fn drop(&mut self) { + unsafe { + match self.function.tag { + CApiExternTag::Function => mem::ManuallyDrop::drop(&mut self.function), + CApiExternTag::Global => mem::ManuallyDrop::drop(&mut self.global), + CApiExternTag::Table => mem::ManuallyDrop::drop(&mut self.table), + CApiExternTag::Memory => mem::ManuallyDrop::drop(&mut self.memory), + } + } + } +} + +impl wasm_extern_t { + pub(crate) fn get_tag(&self) -> CApiExternTag { + unsafe { self.inner.function.tag } + } + + pub(crate) fn ty(&self) -> ExternType { + match self.get_tag() { + CApiExternTag::Function => { + ExternType::Function(unsafe { self.inner.function.inner.ty().clone() }) + } + CApiExternTag::Memory => { + ExternType::Memory(unsafe { self.inner.memory.inner.ty().clone() }) + } + CApiExternTag::Global => { + ExternType::Global(unsafe { self.inner.global.inner.ty().clone() }) + } + CApiExternTag::Table => { + ExternType::Table(unsafe { self.inner.table.inner.ty().clone() }) + } + } + } +} + +impl Clone for wasm_extern_t { + fn clone(&self) -> Self { + match self.get_tag() { + CApiExternTag::Function => Self { + inner: wasm_extern_inner { + function: unsafe { self.inner.function.clone() }, + }, + }, + CApiExternTag::Memory => Self { + inner: wasm_extern_inner { + memory: unsafe { self.inner.memory.clone() }, + }, + }, + CApiExternTag::Global => Self { + inner: wasm_extern_inner { + global: unsafe { self.inner.global.clone() }, + }, + }, + CApiExternTag::Table => Self { + inner: wasm_extern_inner { + table: unsafe { self.inner.table.clone() }, + }, + }, + } + } +} + +impl From for wasm_extern_t { + fn from(other: Extern) -> Self { + match other { + Extern::Function(function) => Self { + inner: wasm_extern_inner { + function: mem::ManuallyDrop::new(wasm_func_t::new(function)), + }, + }, + Extern::Memory(memory) => Self { + inner: wasm_extern_inner { + memory: mem::ManuallyDrop::new(wasm_memory_t::new(memory)), + }, + }, + Extern::Table(table) => Self { + inner: wasm_extern_inner { + table: mem::ManuallyDrop::new(wasm_table_t::new(table)), + }, + }, + Extern::Global(global) => Self { + inner: wasm_extern_inner { + global: mem::ManuallyDrop::new(wasm_global_t::new(global)), + }, + }, + } + } +} + +impl From for Extern { + fn from(other: wasm_extern_t) -> Self { + match other.get_tag() { + CApiExternTag::Function => unsafe { (&*other.inner.function.inner).clone().into() }, + CApiExternTag::Memory => unsafe { (&*other.inner.memory.inner).clone().into() }, + CApiExternTag::Table => unsafe { (&*other.inner.table.inner).clone().into() }, + CApiExternTag::Global => unsafe { (&*other.inner.global.inner).clone().into() }, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(C)] +pub(crate) enum CApiExternTag { + Function, + Global, + Table, + Memory, } wasm_declare_boxed_vec!(extern); @@ -31,106 +182,68 @@ pub unsafe extern "C" fn wasm_extern_copy(r#extern: &wasm_extern_t) -> Box>) {} #[no_mangle] -pub unsafe extern "C" fn wasm_func_as_extern( - func: Option<&wasm_func_t>, -) -> Option> { - let func = func?; - - Some(Box::new(wasm_extern_t { - instance: func.instance.clone(), - inner: Extern::Function(func.inner.clone()), - })) +pub extern "C" fn wasm_func_as_extern(func: Option<&wasm_func_t>) -> Option<&wasm_extern_t> { + unsafe { mem::transmute::, Option<&wasm_extern_t>>(func) } } #[no_mangle] -pub unsafe extern "C" fn wasm_global_as_extern( - global: Option<&wasm_global_t>, -) -> Option> { - let global = global?; - - Some(Box::new(wasm_extern_t { - // TODO: update this if global does hold onto an `instance` - instance: None, - inner: Extern::Global(global.inner.clone()), - })) +pub extern "C" fn wasm_global_as_extern(global: Option<&wasm_global_t>) -> Option<&wasm_extern_t> { + unsafe { mem::transmute::, Option<&wasm_extern_t>>(global) } } #[no_mangle] -pub unsafe extern "C" fn wasm_memory_as_extern( - memory: Option<&wasm_memory_t>, -) -> Option> { - let memory = memory?; - - Some(Box::new(wasm_extern_t { - // TODO: update this if global does hold onto an `instance` - instance: None, - inner: Extern::Memory(memory.inner.clone()), - })) +pub extern "C" fn wasm_memory_as_extern(memory: Option<&wasm_memory_t>) -> Option<&wasm_extern_t> { + unsafe { mem::transmute::, Option<&wasm_extern_t>>(memory) } } #[no_mangle] -pub unsafe extern "C" fn wasm_table_as_extern( - table: Option<&wasm_table_t>, -) -> Option> { - let table = table?; - - Some(Box::new(wasm_extern_t { - // TODO: update this if global does hold onto an `instance` - instance: None, - inner: Extern::Table(table.inner.clone()), - })) +pub extern "C" fn wasm_table_as_extern(table: Option<&wasm_table_t>) -> Option<&wasm_extern_t> { + unsafe { mem::transmute::, Option<&wasm_extern_t>>(table) } } #[no_mangle] -pub unsafe extern "C" fn wasm_extern_as_func( - r#extern: Option<&wasm_extern_t>, -) -> Option> { +pub extern "C" fn wasm_extern_as_func(r#extern: Option<&wasm_extern_t>) -> Option<&wasm_func_t> { let r#extern = r#extern?; - if let Extern::Function(f) = &r#extern.inner { - Some(Box::new(wasm_func_t { - inner: f.clone(), - instance: r#extern.instance.clone(), - })) + if r#extern.get_tag() == CApiExternTag::Function { + Some(unsafe { mem::transmute::<&wasm_extern_t, &wasm_func_t>(r#extern) }) } else { None } } #[no_mangle] -pub unsafe extern "C" fn wasm_extern_as_global( +pub extern "C" fn wasm_extern_as_global( r#extern: Option<&wasm_extern_t>, -) -> Option> { +) -> Option<&wasm_global_t> { let r#extern = r#extern?; - if let Extern::Global(g) = &r#extern.inner { - Some(Box::new(wasm_global_t { inner: g.clone() })) + if r#extern.get_tag() == CApiExternTag::Global { + Some(unsafe { mem::transmute::<&wasm_extern_t, &wasm_global_t>(r#extern) }) } else { None } } #[no_mangle] -pub unsafe extern "C" fn wasm_extern_as_memory( +pub extern "C" fn wasm_extern_as_memory( r#extern: Option<&wasm_extern_t>, -) -> Option> { +) -> Option<&wasm_memory_t> { let r#extern = r#extern?; - if let Extern::Memory(m) = &r#extern.inner { - Some(Box::new(wasm_memory_t { inner: m.clone() })) + if r#extern.get_tag() == CApiExternTag::Memory { + Some(unsafe { mem::transmute::<&wasm_extern_t, &wasm_memory_t>(r#extern) }) } else { None } } #[no_mangle] -pub unsafe extern "C" fn wasm_extern_as_table( - r#extern: Option<&wasm_extern_t>, -) -> Option> { +pub extern "C" fn wasm_extern_as_table(r#extern: Option<&wasm_extern_t>) -> Option<&wasm_table_t> { let r#extern = r#extern?; - if let Extern::Table(t) = &r#extern.inner { - Some(Box::new(wasm_table_t { inner: t.clone() })) + if r#extern.get_tag() == CApiExternTag::Table { + Some(unsafe { mem::transmute::<&wasm_extern_t, &wasm_table_t>(r#extern) }) } else { None } diff --git a/lib/c-api/src/wasm_c_api/externals/table.rs b/lib/c-api/src/wasm_c_api/externals/table.rs index d2b23f5fa..f90f8607d 100644 --- a/lib/c-api/src/wasm_c_api/externals/table.rs +++ b/lib/c-api/src/wasm_c_api/externals/table.rs @@ -1,11 +1,23 @@ use super::super::store::wasm_store_t; use super::super::types::{wasm_ref_t, wasm_table_size_t, wasm_tabletype_t}; +use super::CApiExternTag; use wasmer::Table; #[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Clone)] pub struct wasm_table_t { - // maybe needs to hold onto instance - pub(crate) inner: Table, + pub(crate) tag: CApiExternTag, + pub(crate) inner: Box, +} + +impl wasm_table_t { + pub(crate) fn new(table: Table) -> Self { + Self { + tag: CApiExternTag::Table, + inner: Box::new(table), + } + } } #[no_mangle] @@ -32,9 +44,7 @@ pub unsafe extern "C" fn wasm_table_delete(_table: Option>) {} #[no_mangle] pub unsafe extern "C" fn wasm_table_copy(table: &wasm_table_t) -> Box { // do shallow copy - Box::new(wasm_table_t { - inner: table.inner.clone(), - }) + Box::new(wasm_table_t::new((&*table.inner).clone())) } #[no_mangle] diff --git a/lib/c-api/src/wasm_c_api/instance.rs b/lib/c-api/src/wasm_c_api/instance.rs index 9f7bc3842..1ef0b55d3 100644 --- a/lib/c-api/src/wasm_c_api/instance.rs +++ b/lib/c-api/src/wasm_c_api/instance.rs @@ -53,9 +53,8 @@ pub unsafe extern "C" fn wasm_instance_new( .into_slice() .map(|imports| imports.iter()) .unwrap_or_else(|| [].iter()) - .map(|imp| &imp.inner) + .map(|imp| Extern::from((&**imp).clone())) .take(module_import_count) - .cloned() .collect(); let instance = match Instance::new(wasm_module, &resolver) { @@ -192,10 +191,7 @@ pub unsafe extern "C" fn wasm_instance_exports( None }; - Box::into_raw(Box::new(wasm_extern_t { - instance: Some(Arc::clone(instance)), - inner: r#extern.clone(), - })) + Box::into_raw(Box::new(r#extern.clone().into())) }) .collect::>(); extern_vec.shrink_to_fit(); diff --git a/lib/c-api/src/wasm_c_api/types/export.rs b/lib/c-api/src/wasm_c_api/types/export.rs index 6a78805cb..a2d94d3c0 100644 --- a/lib/c-api/src/wasm_c_api/types/export.rs +++ b/lib/c-api/src/wasm_c_api/types/export.rs @@ -1,10 +1,10 @@ -use super::{wasm_externtype_t, wasm_name_t}; +use super::{owned_wasm_name_t, wasm_externtype_t, wasm_name_t}; use wasmer::ExportType; #[allow(non_camel_case_types)] #[derive(Clone)] pub struct wasm_exporttype_t { - name: Box, + name: owned_wasm_name_t, extern_type: Box, } @@ -12,11 +12,12 @@ wasm_declare_boxed_vec!(exporttype); #[no_mangle] pub extern "C" fn wasm_exporttype_new( - name: Option>, + name: Option<&wasm_name_t>, extern_type: Option>, ) -> Option> { + let name = unsafe { owned_wasm_name_t::new(name?) }; Some(Box::new(wasm_exporttype_t { - name: name?, + name, extern_type: extern_type?, })) } @@ -42,7 +43,7 @@ impl From for wasm_exporttype_t { impl From<&ExportType> for wasm_exporttype_t { fn from(other: &ExportType) -> Self { - let name: Box = Box::new(other.name().to_string().into()); + let name: owned_wasm_name_t = other.name().to_string().into(); let extern_type: Box = Box::new(other.ty().into()); wasm_exporttype_t { name, extern_type } diff --git a/lib/c-api/src/wasm_c_api/types/extern_.rs b/lib/c-api/src/wasm_c_api/types/extern_.rs index 4ef1f8cb6..cdf2abca9 100644 --- a/lib/c-api/src/wasm_c_api/types/extern_.rs +++ b/lib/c-api/src/wasm_c_api/types/extern_.rs @@ -85,12 +85,12 @@ impl From<&ExternType> for wasm_externtype_t { #[no_mangle] pub unsafe extern "C" fn wasm_extern_type(r#extern: &wasm_extern_t) -> Box { - Box::new(wasm_externtype_t::new(r#extern.inner.ty())) + Box::new(wasm_externtype_t::new(r#extern.ty())) } #[no_mangle] pub unsafe extern "C" fn wasm_extern_kind(r#extern: &wasm_extern_t) -> wasm_externkind_t { - wasm_externkind_enum::from(r#extern.inner.ty()) as wasm_externkind_t + wasm_externkind_enum::from(r#extern.ty()) as wasm_externkind_t } #[no_mangle] diff --git a/lib/c-api/src/wasm_c_api/types/import.rs b/lib/c-api/src/wasm_c_api/types/import.rs index 9d060584d..8e2e5361c 100644 --- a/lib/c-api/src/wasm_c_api/types/import.rs +++ b/lib/c-api/src/wasm_c_api/types/import.rs @@ -1,11 +1,12 @@ -use super::{wasm_externtype_t, wasm_name_t}; +use super::{owned_wasm_name_t, wasm_externtype_t, wasm_name_t}; use wasmer::ImportType; #[allow(non_camel_case_types)] #[derive(Clone)] +#[repr(C)] pub struct wasm_importtype_t { - module: Box, - name: Box, + module: owned_wasm_name_t, + name: owned_wasm_name_t, extern_type: Box, } @@ -13,13 +14,19 @@ wasm_declare_boxed_vec!(importtype); #[no_mangle] pub extern "C" fn wasm_importtype_new( - module: Option>, - name: Option>, + module: Option<&wasm_name_t>, + name: Option<&wasm_name_t>, extern_type: Option>, ) -> Option> { + let (module, name) = unsafe { + ( + owned_wasm_name_t::new(module?), + owned_wasm_name_t::new(name?), + ) + }; Some(Box::new(wasm_importtype_t { - name: name?, - module: module?, + name, + module, extern_type: extern_type?, })) } @@ -50,8 +57,8 @@ impl From for wasm_importtype_t { impl From<&ImportType> for wasm_importtype_t { fn from(other: &ImportType) -> Self { - let module: Box = Box::new(other.module().to_string().into()); - let name: Box = Box::new(other.name().to_string().into()); + let module: owned_wasm_name_t = other.module().to_string().into(); + let name: owned_wasm_name_t = other.name().to_string().into(); let extern_type: Box = Box::new(other.ty().into()); wasm_importtype_t { diff --git a/lib/c-api/src/wasm_c_api/types/mod.rs b/lib/c-api/src/wasm_c_api/types/mod.rs index c022ba17e..541747045 100644 --- a/lib/c-api/src/wasm_c_api/types/mod.rs +++ b/lib/c-api/src/wasm_c_api/types/mod.rs @@ -28,16 +28,63 @@ wasm_declare_vec!(byte); #[allow(non_camel_case_types)] pub type wasm_name_t = wasm_byte_vec_t; -impl From for wasm_name_t { +impl AsRef for wasm_name_t { + fn as_ref(&self) -> &wasm_name_t { + &self + } +} + +/// An owned version of `wasm_name_t`. +/// +/// Assumes that data is either valid host-owned or null. +// NOTE: `wasm_name_t` already does a deep copy, so we just derive `Clone` here. +#[derive(Debug, Clone)] +#[repr(transparent)] +#[allow(non_camel_case_types)] +pub struct owned_wasm_name_t(wasm_name_t); + +impl owned_wasm_name_t { + /// Take ownership of some `wasm_name_t` + /// + /// # Safety + /// You must ensure that the data pointed to by `wasm_name_t` is valid and + /// that it is not owned by anyone else. + pub unsafe fn new(name: &wasm_name_t) -> Self { + Self(wasm_name_t { + size: name.size, + data: name.data, + }) + } +} + +impl Drop for owned_wasm_name_t { + fn drop(&mut self) { + if !self.0.data.is_null() { + let _v = unsafe { Vec::from_raw_parts(self.0.data, self.0.size, self.0.size) }; + self.0.data = std::ptr::null_mut(); + self.0.size = 0; + } + // why can't we call this function? + //unsafe { crate::wasm_c_api::macros::wasm_byte_vec_delete(Some(self.0)) } + } +} + +impl AsRef for owned_wasm_name_t { + fn as_ref(&self) -> &wasm_name_t { + &self.0 + } +} + +impl From for owned_wasm_name_t { fn from(string: String) -> Self { let mut boxed_str: Box = string.into_boxed_str(); let data = boxed_str.as_mut_ptr(); let size = boxed_str.bytes().len(); - let wasm_name = Self { data, size }; + let wasm_name = wasm_name_t { data, size }; Box::leak(boxed_str); - wasm_name + Self(wasm_name) } } diff --git a/lib/c-api/src/wasm_c_api/unstable/wasi.rs b/lib/c-api/src/wasm_c_api/unstable/wasi.rs index 8eb0a5fa2..8b3801be8 100644 --- a/lib/c-api/src/wasm_c_api/unstable/wasi.rs +++ b/lib/c-api/src/wasm_c_api/unstable/wasi.rs @@ -2,7 +2,10 @@ //! API. use super::super::{ - externals::wasm_extern_t, module::wasm_module_t, store::wasm_store_t, types::wasm_name_t, + externals::wasm_extern_t, + module::wasm_module_t, + store::wasm_store_t, + types::{owned_wasm_name_t, wasm_name_t}, wasi::wasi_env_t, }; use crate::error::CApiError; @@ -19,8 +22,8 @@ use wasmer_wasi::{generate_import_object_from_env, get_wasi_version}; #[allow(non_camel_case_types)] #[derive(Clone)] pub struct wasmer_named_extern_t { - module: Box, - name: Box, + module: owned_wasm_name_t, + name: owned_wasm_name_t, r#extern: Box, } @@ -179,17 +182,14 @@ fn wasi_get_unordered_imports_inner( *imports = import_object .into_iter() .map(|((module, name), export)| { - let module = Box::new(module.into()); - let name = Box::new(name.into()); + let module = module.into(); + let name = name.into(); let extern_inner = Extern::from_vm_export(store, export); Box::new(wasmer_named_extern_t { module, name, - r#extern: Box::new(wasm_extern_t { - instance: None, - inner: extern_inner, - }), + r#extern: Box::new(extern_inner.into()), }) }) .collect::>() diff --git a/lib/c-api/src/wasm_c_api/wasi/mod.rs b/lib/c-api/src/wasm_c_api/wasi/mod.rs index 5d16fabb0..1097665b2 100644 --- a/lib/c-api/src/wasm_c_api/wasi/mod.rs +++ b/lib/c-api/src/wasm_c_api/wasi/mod.rs @@ -6,7 +6,7 @@ mod capture_files; pub use super::unstable::wasi::wasi_get_unordered_imports; use super::{ - externals::{wasm_extern_t, wasm_extern_vec_t, wasm_func_t, wasm_memory_t}, + externals::{wasm_extern_vec_t, wasm_func_t, wasm_memory_t}, instance::wasm_instance_t, module::wasm_module_t, store::wasm_store_t, @@ -390,10 +390,7 @@ fn wasi_get_imports_inner( })); let inner = Extern::from_vm_export(store, export); - Some(Box::new(wasm_extern_t { - instance: None, - inner, - })) + Some(Box::new(inner.into())) }) .collect::>>()? .into(); @@ -407,10 +404,7 @@ pub unsafe extern "C" fn wasi_get_start_function( ) -> Option> { let start = c_try!(instance.inner.exports.get_function("_start")); - Some(Box::new(wasm_func_t { - inner: start.clone(), - instance: Some(instance.inner.clone()), - })) + Some(Box::new(wasm_func_t::new(start.clone()))) } #[cfg(test)] diff --git a/lib/c-api/tests/test-early-exit.c b/lib/c-api/tests/test-early-exit.c index d98b5417c..429ef3b96 100644 --- a/lib/c-api/tests/test-early-exit.c +++ b/lib/c-api/tests/test-early-exit.c @@ -90,8 +90,6 @@ int main(int argc, const char *argv[]) { return 1; } - wasm_func_delete(host_func); - // Extract export. printf("Extracting export...\n"); own wasm_extern_vec_t exports; diff --git a/lib/cli/src/commands/config.rs b/lib/cli/src/commands/config.rs index 7b224265c..888bc9b20 100644 --- a/lib/cli/src/commands/config.rs +++ b/lib/cli/src/commands/config.rs @@ -8,27 +8,27 @@ use std::path::PathBuf; /// The options for the `wasmer config` subcommand pub struct Config { /// Print the installation prefix. - #[clap(long, conflicts_with = "pkg_config")] + #[clap(long, conflicts_with = "pkg-config")] prefix: bool, /// Directory containing Wasmer executables. - #[clap(long, conflicts_with = "pkg_config")] + #[clap(long, conflicts_with = "pkg-config")] bindir: bool, /// Directory containing Wasmer headers. - #[clap(long, conflicts_with = "pkg_config")] + #[clap(long, conflicts_with = "pkg-config")] includedir: bool, /// Directory containing Wasmer libraries. - #[clap(long, conflicts_with = "pkg_config")] + #[clap(long, conflicts_with = "pkg-config")] libdir: bool, /// Libraries needed to link against Wasmer components. - #[clap(long, conflicts_with = "pkg_config")] + #[clap(long, conflicts_with = "pkg-config")] libs: bool, /// C compiler flags for files that include Wasmer headers. - #[clap(long, conflicts_with = "pkg_config")] + #[clap(long, conflicts_with = "pkg-config")] cflags: bool, /// It outputs the necessary details for compiling diff --git a/lib/cli/src/store.rs b/lib/cli/src/store.rs index 8c8824ce2..115ba4d08 100644 --- a/lib/cli/src/store.rs +++ b/lib/cli/src/store.rs @@ -20,11 +20,11 @@ pub struct StoreOptions { compiler: CompilerOptions, /// Use JIT Engine. - #[clap(long, conflicts_with_all = &["native", "object_file"])] + #[clap(long, conflicts_with_all = &["native", "object-file"])] jit: bool, /// Use Native Engine. - #[clap(long, conflicts_with_all = &["jit", "object_file"])] + #[clap(long, conflicts_with_all = &["jit", "object-file"])] native: bool, /// Use ObjectFile Engine. @@ -212,18 +212,19 @@ impl CompilerOptions { // Converts a kind into a filename, that we will use to dump // the contents of the IR object file to. fn types_to_signature(types: &[Type]) -> String { - types.iter().map(|ty| { - match ty { + types + .iter() + .map(|ty| match ty { Type::I32 => "i".to_string(), Type::I64 => "I".to_string(), Type::F32 => "f".to_string(), Type::F64 => "F".to_string(), Type::V128 => "v".to_string(), - _ => { - unimplemented!("Function type not yet supported for generated signatures in debugging"); - } - } - }).collect::>().join("") + Type::ExternRef => "e".to_string(), + Type::FuncRef => "r".to_string(), + }) + .collect::>() + .join("") } // Converts a kind into a filename, that we will use to dump // the contents of the IR object file to. diff --git a/lib/compiler-cranelift/src/config.rs b/lib/compiler-cranelift/src/config.rs index ed8419c2f..6834e1d52 100644 --- a/lib/compiler-cranelift/src/config.rs +++ b/lib/compiler-cranelift/src/config.rs @@ -152,6 +152,9 @@ impl Cranelift { flags .set("enable_verifier", enable_verifier) .expect("should be valid flag"); + flags + .set("enable_safepoints", "true") + .expect("should be valid flag"); let opt_level = if self.enable_simd { "none" diff --git a/lib/compiler-cranelift/src/func_environ.rs b/lib/compiler-cranelift/src/func_environ.rs index 9d9161d2c..56e451f4c 100644 --- a/lib/compiler-cranelift/src/func_environ.rs +++ b/lib/compiler-cranelift/src/func_environ.rs @@ -11,13 +11,16 @@ use cranelift_codegen::ir::immediates::{Offset32, Uimm64}; use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::{AbiParam, ArgumentPurpose, Function, InstBuilder, Signature}; use cranelift_codegen::isa::TargetFrontendConfig; -use cranelift_frontend::FunctionBuilder; +use cranelift_frontend::{FunctionBuilder, Variable}; use std::convert::TryFrom; use wasmer_compiler::wasmparser::Type; use wasmer_compiler::{WasmError, WasmResult}; use wasmer_types::entity::EntityRef; use wasmer_types::entity::PrimaryMap; -use wasmer_types::{FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex}; +use wasmer_types::{ + FunctionIndex, FunctionType, GlobalIndex, LocalFunctionIndex, MemoryIndex, SignatureIndex, + TableIndex, Type as WasmerType, +}; use wasmer_vm::VMBuiltinFunctionIndex; use wasmer_vm::VMOffsets; use wasmer_vm::{MemoryStyle, ModuleInfo, TableStyle}; @@ -45,6 +48,9 @@ pub struct FuncEnvironment<'module_environment> { /// The module-level environment which this function-level environment belongs to. module: &'module_environment ModuleInfo, + /// A stack tracking the type of local variables. + type_stack: Vec, + /// The module function signatures signatures: &'module_environment PrimaryMap, @@ -55,10 +61,18 @@ pub struct FuncEnvironment<'module_environment> { /// for locally-defined 32-bit memories. memory32_size_sig: Option, + /// The external function signature for implementing wasm's `table.size` + /// for locally-defined tables. + table_size_sig: Option, + /// The external function signature for implementing wasm's `memory.grow` /// for locally-defined memories. memory_grow_sig: Option, + /// The external function signature for implementing wasm's `table.grow` + /// for locally-defined tables. + table_grow_sig: Option, + /// The external function signature for implementing wasm's `table.copy` /// (it's the same for both local and imported tables). table_copy_sig: Option, @@ -83,6 +97,23 @@ pub struct FuncEnvironment<'module_environment> { /// The external function signature for implementing wasm's `data.drop`. data_drop_sig: Option, + /// The external function signature for implementing wasm's `table.get`. + table_get_sig: Option, + + /// The external function signature for implementing wasm's `table.set`. + table_set_sig: Option, + + /// The external function signature for implementing wasm's `func.ref`. + func_ref_sig: Option, + + /// The external function signature for implementing wasm's `table.fill`. + table_fill_sig: Option, + + /// The external function signature for implementing reference increment for `extern.ref`. + externref_inc_sig: Option, + + /// The external function signature for implementing reference decrement for `extern.ref`. + externref_dec_sig: Option, /// Offsets to struct fields accessed by JIT code. offsets: VMOffsets, @@ -105,16 +136,25 @@ impl<'module_environment> FuncEnvironment<'module_environment> { target_config, module, signatures, + type_stack: vec![], vmctx: None, memory32_size_sig: None, + table_size_sig: None, memory_grow_sig: None, + table_grow_sig: None, table_copy_sig: None, table_init_sig: None, elem_drop_sig: None, memory_copy_sig: None, memory_fill_sig: None, memory_init_sig: None, + table_get_sig: None, + table_set_sig: None, data_drop_sig: None, + func_ref_sig: None, + table_fill_sig: None, + externref_inc_sig: None, + externref_dec_sig: None, offsets: VMOffsets::new(target_config.pointer_bytes(), module), memory_styles, table_styles, @@ -133,6 +173,224 @@ impl<'module_environment> FuncEnvironment<'module_environment> { }) } + fn get_table_fill_sig(&mut self, func: &mut Function) -> ir::SigRef { + let sig = self.table_fill_sig.unwrap_or_else(|| { + func.import_signature(Signature { + params: vec![ + AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), + // table index + AbiParam::new(I32), + // dst + AbiParam::new(I32), + // value + AbiParam::new(R64), + // len + AbiParam::new(I32), + ], + returns: vec![], + call_conv: self.target_config.default_call_conv, + }) + }); + self.table_fill_sig = Some(sig); + sig + } + + fn get_table_fill_func( + &mut self, + func: &mut Function, + table_index: TableIndex, + ) -> (ir::SigRef, usize, VMBuiltinFunctionIndex) { + ( + self.get_table_fill_sig(func), + table_index.index(), + VMBuiltinFunctionIndex::get_table_fill_index(), + ) + } + + fn get_externref_inc_sig(&mut self, func: &mut Function) -> ir::SigRef { + let sig = self.externref_inc_sig.unwrap_or_else(|| { + func.import_signature(Signature { + params: vec![AbiParam::new(R64)], + returns: vec![], + call_conv: self.target_config.default_call_conv, + }) + }); + self.externref_inc_sig = Some(sig); + sig + } + + fn get_externref_inc_func( + &mut self, + func: &mut Function, + ) -> (ir::SigRef, VMBuiltinFunctionIndex) { + ( + self.get_externref_inc_sig(func), + VMBuiltinFunctionIndex::get_externref_inc_index(), + ) + } + + fn get_externref_dec_sig(&mut self, func: &mut Function) -> ir::SigRef { + let sig = self.externref_dec_sig.unwrap_or_else(|| { + func.import_signature(Signature { + params: vec![AbiParam::new(R64)], + returns: vec![], + call_conv: self.target_config.default_call_conv, + }) + }); + self.externref_dec_sig = Some(sig); + sig + } + + fn get_externref_dec_func( + &mut self, + func: &mut Function, + ) -> (ir::SigRef, VMBuiltinFunctionIndex) { + ( + self.get_externref_dec_sig(func), + VMBuiltinFunctionIndex::get_externref_dec_index(), + ) + } + + fn get_func_ref_sig(&mut self, func: &mut Function) -> ir::SigRef { + let sig = self.func_ref_sig.unwrap_or_else(|| { + func.import_signature(Signature { + params: vec![ + AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), + AbiParam::new(I32), + ], + returns: vec![AbiParam::new(R64)], + call_conv: self.target_config.default_call_conv, + }) + }); + self.func_ref_sig = Some(sig); + sig + } + + fn get_func_ref_func( + &mut self, + func: &mut Function, + function_index: FunctionIndex, + ) -> (ir::SigRef, usize, VMBuiltinFunctionIndex) { + ( + self.get_func_ref_sig(func), + function_index.index(), + VMBuiltinFunctionIndex::get_func_ref_index(), + ) + } + + fn get_table_get_sig(&mut self, func: &mut Function) -> ir::SigRef { + let sig = self.table_get_sig.unwrap_or_else(|| { + func.import_signature(Signature { + params: vec![ + AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), + AbiParam::new(I32), + AbiParam::new(I32), + ], + returns: vec![AbiParam::new(R64)], + call_conv: self.target_config.default_call_conv, + }) + }); + self.table_get_sig = Some(sig); + sig + } + + fn get_table_get_func( + &mut self, + func: &mut Function, + table_index: TableIndex, + ) -> (ir::SigRef, usize, VMBuiltinFunctionIndex) { + if self.module.is_imported_table(table_index) { + ( + self.get_table_get_sig(func), + table_index.index(), + VMBuiltinFunctionIndex::get_imported_table_get_index(), + ) + } else { + ( + self.get_table_get_sig(func), + self.module.local_table_index(table_index).unwrap().index(), + VMBuiltinFunctionIndex::get_table_get_index(), + ) + } + } + + fn get_table_set_sig(&mut self, func: &mut Function) -> ir::SigRef { + let sig = self.table_set_sig.unwrap_or_else(|| { + func.import_signature(Signature { + params: vec![ + AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), + AbiParam::new(I32), + AbiParam::new(I32), + AbiParam::new(R64), + ], + returns: vec![], + call_conv: self.target_config.default_call_conv, + }) + }); + self.table_set_sig = Some(sig); + sig + } + + fn get_table_set_func( + &mut self, + func: &mut Function, + table_index: TableIndex, + ) -> (ir::SigRef, usize, VMBuiltinFunctionIndex) { + if self.module.is_imported_table(table_index) { + ( + self.get_table_set_sig(func), + table_index.index(), + VMBuiltinFunctionIndex::get_imported_table_set_index(), + ) + } else { + ( + self.get_table_set_sig(func), + self.module.local_table_index(table_index).unwrap().index(), + VMBuiltinFunctionIndex::get_table_set_index(), + ) + } + } + + fn get_table_grow_sig(&mut self, func: &mut Function) -> ir::SigRef { + let sig = self.table_grow_sig.unwrap_or_else(|| { + func.import_signature(Signature { + params: vec![ + AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), + // TODO: figure out what the representation of a Wasm value is + AbiParam::new(R64), + AbiParam::new(I32), + AbiParam::new(I32), + ], + returns: vec![AbiParam::new(I32)], + call_conv: self.target_config.default_call_conv, + }) + }); + self.table_grow_sig = Some(sig); + sig + } + + /// Return the table.grow function signature to call for the given index, along with the + /// translated index value to pass to it and its index in `VMBuiltinFunctionsArray`. + fn get_table_grow_func( + &mut self, + func: &mut Function, + index: TableIndex, + ) -> (ir::SigRef, usize, VMBuiltinFunctionIndex) { + if self.module.is_imported_table(index) { + ( + self.get_table_grow_sig(func), + index.index(), + VMBuiltinFunctionIndex::get_imported_table_grow_index(), + ) + } else { + ( + self.get_table_grow_sig(func), + self.module.local_table_index(index).unwrap().index(), + VMBuiltinFunctionIndex::get_table_grow_index(), + ) + } + } + fn get_memory_grow_sig(&mut self, func: &mut Function) -> ir::SigRef { let sig = self.memory_grow_sig.unwrap_or_else(|| { func.import_signature(Signature { @@ -171,6 +429,43 @@ impl<'module_environment> FuncEnvironment<'module_environment> { } } + fn get_table_size_sig(&mut self, func: &mut Function) -> ir::SigRef { + let sig = self.table_size_sig.unwrap_or_else(|| { + func.import_signature(Signature { + params: vec![ + AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), + AbiParam::new(I32), + ], + returns: vec![AbiParam::new(I32)], + call_conv: self.target_config.default_call_conv, + }) + }); + self.table_size_sig = Some(sig); + sig + } + + /// Return the memory.size function signature to call for the given index, along with the + /// translated index value to pass to it and its index in `VMBuiltinFunctionsArray`. + fn get_table_size_func( + &mut self, + func: &mut Function, + index: TableIndex, + ) -> (ir::SigRef, usize, VMBuiltinFunctionIndex) { + if self.module.is_imported_table(index) { + ( + self.get_table_size_sig(func), + index.index(), + VMBuiltinFunctionIndex::get_imported_table_size_index(), + ) + } else { + ( + self.get_table_size_sig(func), + self.module.local_table_index(index).unwrap().index(), + VMBuiltinFunctionIndex::get_table_size_index(), + ) + } + } + fn get_memory32_size_sig(&mut self, func: &mut Function) -> ir::SigRef { let sig = self.memory32_size_sig.unwrap_or_else(|| { func.import_signature(Signature { @@ -338,7 +633,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> { ( sig, local_memory_index.index(), - VMBuiltinFunctionIndex::get_local_memory_copy_index(), + VMBuiltinFunctionIndex::get_memory_copy_index(), ) } else { ( @@ -524,9 +819,7 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro }); let element_size = match self.table_styles[index] { - TableStyle::CallerChecksSignature => { - u64::from(self.offsets.size_of_vmcaller_checked_anyfunc()) - } + TableStyle::CallerChecksSignature => u64::from(self.offsets.size_of_vm_funcref()), }; Ok(func.create_table(ir::TableData { @@ -540,53 +833,107 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro fn translate_table_grow( &mut self, - mut _pos: cranelift_codegen::cursor::FuncCursor<'_>, - _table_index: TableIndex, + mut pos: cranelift_codegen::cursor::FuncCursor<'_>, + table_index: TableIndex, _table: ir::Table, - _delta: ir::Value, - _init_value: ir::Value, + delta: ir::Value, + init_value: ir::Value, ) -> WasmResult { - Err(WasmError::Unsupported( - "the `table.grow` instruction is not supported yet".into(), - )) + let (func_sig, index_arg, func_idx) = self.get_table_grow_func(&mut pos.func, table_index); + let table_index = pos.ins().iconst(I32, index_arg as i64); + let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx); + let call_inst = pos.ins().call_indirect( + func_sig, + func_addr, + &[vmctx, init_value, delta, table_index], + ); + Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) } fn translate_table_get( &mut self, - _builder: &mut FunctionBuilder, - _table_index: TableIndex, + builder: &mut FunctionBuilder, + table_index: TableIndex, _table: ir::Table, - _index: ir::Value, + index: ir::Value, ) -> WasmResult { - Err(WasmError::Unsupported( - "the `table.get` instruction is not supported yet".into(), - )) + let mut pos = builder.cursor(); + + let (func_sig, table_index_arg, func_idx) = + self.get_table_get_func(&mut pos.func, table_index); + let table_index = pos.ins().iconst(I32, table_index_arg as i64); + let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx); + let call_inst = pos + .ins() + .call_indirect(func_sig, func_addr, &[vmctx, table_index, index]); + Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) } fn translate_table_set( &mut self, - _builder: &mut FunctionBuilder, - _table_index: TableIndex, + builder: &mut FunctionBuilder, + table_index: TableIndex, _table: ir::Table, - _value: ir::Value, - _index: ir::Value, + value: ir::Value, + index: ir::Value, ) -> WasmResult<()> { - Err(WasmError::Unsupported( - "the `table.set` instruction is not supported yet".into(), - )) + let mut pos = builder.cursor(); + + let (func_sig, table_index_arg, func_idx) = + self.get_table_set_func(&mut pos.func, table_index); + let table_index = pos.ins().iconst(I32, table_index_arg as i64); + let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx); + pos.ins() + .call_indirect(func_sig, func_addr, &[vmctx, table_index, index, value]); + Ok(()) } fn translate_table_fill( &mut self, - mut _pos: cranelift_codegen::cursor::FuncCursor<'_>, - _table_index: TableIndex, - _dst: ir::Value, - _val: ir::Value, - _len: ir::Value, + mut pos: cranelift_codegen::cursor::FuncCursor<'_>, + table_index: TableIndex, + dst: ir::Value, + val: ir::Value, + len: ir::Value, ) -> WasmResult<()> { - Err(WasmError::Unsupported( - "the `table.fill` instruction is not supported yet".into(), - )) + let (func_sig, table_index_arg, func_idx) = + self.get_table_fill_func(&mut pos.func, table_index); + let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx); + + let table_index_arg = pos.ins().iconst(I32, table_index_arg as i64); + pos.ins().call_indirect( + func_sig, + func_addr, + &[vmctx, table_index_arg, dst, val, len], + ); + + Ok(()) + } + + fn translate_externref_inc( + &mut self, + mut pos: cranelift_codegen::cursor::FuncCursor<'_>, + externref: ir::Value, + ) -> WasmResult<()> { + let (func_sig, func_idx) = self.get_externref_inc_func(&mut pos.func); + let (_vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx); + + pos.ins().call_indirect(func_sig, func_addr, &[externref]); + + Ok(()) + } + + fn translate_externref_dec( + &mut self, + mut pos: cranelift_codegen::cursor::FuncCursor<'_>, + externref: ir::Value, + ) -> WasmResult<()> { + let (func_sig, func_idx) = self.get_externref_dec_func(&mut pos.func); + let (_vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx); + + pos.ins().call_indirect(func_sig, func_addr, &[externref]); + + Ok(()) } fn translate_ref_null( @@ -595,11 +942,11 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro ty: Type, ) -> WasmResult { Ok(match ty { - Type::FuncRef => pos.ins().iconst(self.pointer_type(), 0), - // Type::ExternRef => pos.ins().null(self.reference_type(ty)), + Type::FuncRef => pos.ins().null(self.reference_type()), + Type::ExternRef => pos.ins().null(self.reference_type()), _ => { return Err(WasmError::Unsupported( - "`ref.null T` that is not a `funcref`".into(), + "`ref.null T` that is not a `funcref` or an `externref`".into(), )); } }) @@ -626,12 +973,27 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro fn translate_ref_func( &mut self, - mut _pos: cranelift_codegen::cursor::FuncCursor<'_>, - _func_index: FunctionIndex, + mut pos: cranelift_codegen::cursor::FuncCursor<'_>, + func_index: FunctionIndex, ) -> WasmResult { - Err(WasmError::Unsupported( - "the `ref.func` instruction is not supported yet".into(), - )) + // TODO: optimize this by storing a pointer to local func_index funcref metadata + // so that local funcref is just (*global + offset) instead of a function call + // + // Actually we can do the above for both local and imported functions because + // all of those are known statically. + // + // prototyping with a function call though + + let (func_sig, func_index_arg, func_idx) = + self.get_func_ref_func(&mut pos.func, func_index); + let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx); + + let func_index_arg = pos.ins().iconst(I32, func_index_arg as i64); + let call_inst = pos + .ins() + .call_indirect(func_sig, func_addr, &[vmctx, func_index_arg]); + + Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) } fn translate_custom_global_get( @@ -797,6 +1159,17 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro // Dereference table_entry_addr to get the function address. let mem_flags = ir::MemFlags::trusted(); + let table_entry_addr = pos.ins().load( + pointer_type, + mem_flags, + table_entry_addr, + i32::from(self.offsets.vm_funcref_anyfunc_ptr()), + ); + + // check if the funcref is null + pos.ins() + .trapz(table_entry_addr, ir::TrapCode::IndirectCallToNull); + let func_addr = pos.ins().load( pointer_type, mem_flags, @@ -804,9 +1177,6 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro i32::from(self.offsets.vmcaller_checked_anyfunc_func_ptr()), ); - // Check whether `func_addr` is null. - pos.ins().trapz(func_addr, ir::TrapCode::IndirectCallToNull); - // If necessary, check the signature. match self.table_styles[table_index] { TableStyle::CallerChecksSignature => { @@ -1019,13 +1389,17 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro fn translate_table_size( &mut self, - mut _pos: FuncCursor, - _table_index: TableIndex, + mut pos: FuncCursor, + table_index: TableIndex, _table: ir::Table, ) -> WasmResult { - Err(WasmError::Unsupported( - "bulk memory: `table.size`".to_string(), - )) + let (func_sig, index_arg, func_idx) = self.get_table_size_func(&mut pos.func, table_index); + let table_index = pos.ins().iconst(I32, index_arg as i64); + let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx); + let call_inst = pos + .ins() + .call_indirect(func_sig, func_addr, &[vmctx, table_index]); + Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) } fn translate_table_copy( @@ -1129,4 +1503,50 @@ impl<'module_environment> BaseFuncEnvironment for FuncEnvironment<'module_enviro "wasm atomics (fn translate_atomic_notify)".to_string(), )) } + + fn get_global_type(&self, global_index: GlobalIndex) -> Option { + Some(self.module.globals.get(global_index)?.ty) + } + + fn push_local_decl_on_stack(&mut self, ty: WasmerType) { + self.type_stack.push(ty); + } + + fn push_params_on_stack(&mut self, function_index: LocalFunctionIndex) { + let func_index = self.module.func_index(function_index); + let sig_idx = self.module.functions[func_index]; + let signature = &self.module.signatures[sig_idx]; + for param in signature.params() { + self.type_stack.push(*param); + } + } + + fn get_local_type(&self, local_index: u32) -> Option { + self.type_stack.get(local_index as usize).cloned() + } + + fn get_local_types(&self) -> &[WasmerType] { + &self.type_stack + } + + fn get_function_type(&self, function_index: FunctionIndex) -> Option<&FunctionType> { + let sig_idx = self.module.functions.get(function_index)?; + Some(&self.module.signatures[*sig_idx]) + } + + fn get_function_sig(&self, sig_index: SignatureIndex) -> Option<&FunctionType> { + self.module.signatures.get(sig_index) + } + + fn translate_drop_locals(&mut self, builder: &mut FunctionBuilder) -> WasmResult<()> { + // TODO: this allocation can be removed without too much effort but it will require + // maneuvering around the borrow checker + for (local_index, local_type) in self.type_stack.to_vec().iter().enumerate() { + if *local_type == WasmerType::ExternRef { + let val = builder.use_var(Variable::with_u32(local_index as _)); + self.translate_externref_dec(builder.cursor(), val)?; + } + } + Ok(()) + } } diff --git a/lib/compiler-cranelift/src/translator/code_translator.rs b/lib/compiler-cranelift/src/translator/code_translator.rs index b49902c27..63678f201 100644 --- a/lib/compiler-cranelift/src/translator/code_translator.rs +++ b/lib/compiler-cranelift/src/translator/code_translator.rs @@ -27,7 +27,7 @@ //! argument. use super::func_environ::{FuncEnvironment, GlobalVariable, ReturnMode}; -use super::func_state::{ControlStackFrame, ElseData, FuncTranslationState}; +use super::func_state::{ControlStackFrame, ElseData, FuncTranslationState, ValueExtraInfo}; use super::translation_utils::{block_with_params, f32_translation, f64_translation}; use crate::{hash_map, HashMap}; use core::cmp; @@ -44,10 +44,12 @@ use cranelift_frontend::{FunctionBuilder, Variable}; use smallvec::SmallVec; use std::vec::Vec; -use wasmer_compiler::wasmparser::{MemoryImmediate, Operator}; +use wasmer_compiler::wasmparser::{MemoryImmediate, Operator, Type as WPType}; use wasmer_compiler::WasmResult; use wasmer_compiler::{wasm_unsupported, ModuleTranslationState}; -use wasmer_types::{FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex}; +use wasmer_types::{ + FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex, Type as WasmerType, +}; // Clippy warns about "align: _" but its important to document that the align field is ignored #[cfg_attr( @@ -76,13 +78,31 @@ pub fn translate_operator( ***********************************************************************************/ Operator::LocalGet { local_index } => { let val = builder.use_var(Variable::with_u32(*local_index)); - state.push1(val); + let local_type = environ.get_local_type(*local_index).unwrap(); + let ref_counted = local_type == WasmerType::ExternRef; + state.push1_extra(( + val, + ValueExtraInfo { + ref_counted, + ..Default::default() + }, + )); let label = ValueLabel::from_u32(*local_index); builder.set_val_label(val, label); + + if ref_counted { + environ.translate_externref_inc(builder.cursor(), val)?; + } } Operator::LocalSet { local_index } => { - let mut val = state.pop1(); + let (mut val, _metadata) = state.pop1(); + let local_type = environ.get_local_type(*local_index).unwrap(); + if local_type == WasmerType::ExternRef { + debug_assert!(_metadata.ref_counted); + let existing_val = builder.use_var(Variable::with_u32(*local_index)); + environ.translate_externref_dec(builder.cursor(), existing_val)?; + } // Ensure SIMD values are cast to their default Cranelift type, I8x16. let ty = builder.func.dfg.value_type(val); if ty.is_vector() { @@ -94,8 +114,16 @@ pub fn translate_operator( builder.set_val_label(val, label); } Operator::LocalTee { local_index } => { - let mut val = state.peek1(); + let (mut val, _metadata) = state.peek1(); + // ref count if we need to + let local_type = environ.get_local_type(*local_index).unwrap(); + if local_type == WasmerType::ExternRef { + debug_assert!(_metadata.ref_counted); + let existing_val = builder.use_var(Variable::with_u32(*local_index)); + environ.translate_externref_dec(builder.cursor(), existing_val)?; + environ.translate_externref_inc(builder.cursor(), val)?; + } // Ensure SIMD values are cast to their default Cranelift type, I8x16. let ty = builder.func.dfg.value_type(val); if ty.is_vector() { @@ -110,41 +138,59 @@ pub fn translate_operator( * `get_global` and `set_global` are handled by the environment. ***********************************************************************************/ Operator::GlobalGet { global_index } => { - let val = match state.get_global(builder.func, *global_index, environ)? { - GlobalVariable::Const(val) => val, + let global_index = GlobalIndex::from_u32(*global_index); + let stack_elem = match state.get_global(builder.func, global_index.as_u32(), environ)? { + GlobalVariable::Const(val) => (val, ValueExtraInfo::default()), GlobalVariable::Memory { gv, offset, ty } => { + let global_type = environ.get_global_type(global_index).unwrap(); let addr = builder.ins().global_value(environ.pointer_type(), gv); let flags = ir::MemFlags::trusted(); - builder.ins().load(ty, flags, addr, offset) + let value = builder.ins().load(ty, flags, addr, offset); + let ref_counted = global_type == WasmerType::ExternRef; + if ref_counted { + environ.translate_externref_inc(builder.cursor(), value)?; + } + + ( + value, + ValueExtraInfo { + ref_counted, + ..Default::default() + }, + ) } - GlobalVariable::Custom => environ.translate_custom_global_get( - builder.cursor(), - GlobalIndex::from_u32(*global_index), - )?, + GlobalVariable::Custom => ( + environ.translate_custom_global_get(builder.cursor(), global_index)?, + ValueExtraInfo::default(), + ), }; - state.push1(val); + state.push1_extra(stack_elem); } Operator::GlobalSet { global_index } => { - match state.get_global(builder.func, *global_index, environ)? { - GlobalVariable::Const(_) => panic!("global #{} is a constant", *global_index), + let global_index = GlobalIndex::from_u32(*global_index); + match state.get_global(builder.func, global_index.as_u32(), environ)? { + GlobalVariable::Const(_) => { + panic!("global #{} is a constant", global_index.as_u32()) + } GlobalVariable::Memory { gv, offset, ty } => { + let global_type = environ.get_global_type(global_index).unwrap(); let addr = builder.ins().global_value(environ.pointer_type(), gv); let flags = ir::MemFlags::trusted(); - let mut val = state.pop1(); + let (mut val, _) = state.pop1(); // Ensure SIMD values are cast to their default Cranelift type, I8x16. if ty.is_vector() { val = optionally_bitcast_vector(val, I8X16, builder); } debug_assert_eq!(ty, builder.func.dfg.value_type(val)); + if global_type == WasmerType::ExternRef { + let value = builder.ins().load(ty, flags, addr, offset); + environ.translate_externref_dec(builder.cursor(), value)?; + } builder.ins().store(flags, val, addr, offset); } GlobalVariable::Custom => { - let val = state.pop1(); - environ.translate_custom_global_set( - builder.cursor(), - GlobalIndex::from_u32(*global_index), - val, - )?; + let (val, _) = state.pop1(); + environ.translate_custom_global_set(builder.cursor(), global_index, val)?; } } } @@ -152,18 +198,36 @@ pub fn translate_operator( * `drop`, `nop`, `unreachable` and `select`. ***********************************************************************************/ Operator::Drop => { - state.pop1(); + let (val, metadata) = state.pop1(); + if metadata.ref_counted { + environ.translate_externref_dec(builder.cursor(), val)?; + } } Operator::Select => { - let (arg1, arg2, cond) = state.pop3(); + // we can ignore metadata because extern ref must use TypedSelect + let ((arg1, _), (arg2, _), (cond, _)) = state.pop3(); state.push1(builder.ins().select(cond, arg1, arg2)); } - Operator::TypedSelect { ty: _ } => { - // We ignore the explicit type parameter as it is only needed for - // validation, which we require to have been performed before - // translation. - let (arg1, arg2, cond) = state.pop3(); - state.push1(builder.ins().select(cond, arg1, arg2)); + Operator::TypedSelect { ty } => { + let ((arg1, _), (arg2, _), (cond, _)) = state.pop3(); + let ref_counted = *ty == WPType::ExternRef; + if ref_counted { + let selected_ref = builder.ins().select(cond, arg1, arg2); + let not_selected_ref = builder.ins().select(cond, arg2, arg1); + state.push1_extra(( + selected_ref, + ValueExtraInfo { + ref_counted, + ..Default::default() + }, + )); + environ.translate_externref_dec(builder.cursor(), not_selected_ref)?; + } else { + state.push1_extra(( + builder.ins().select(cond, arg1, arg2), + ValueExtraInfo::default(), + )); + } } Operator::Nop => { // We do nothing @@ -206,7 +270,7 @@ pub fn translate_operator( environ.translate_loop_header(builder.cursor())?; } Operator::If { ty } => { - let val = state.pop1(); + let (val, _) = state.pop1(); let (params, results) = module_translation_state.blocktype_params_results(*ty)?; let (destination, else_data) = if params == results { @@ -231,7 +295,7 @@ pub fn translate_operator( }; let next_block = builder.create_block(); - canonicalise_then_jump(builder, next_block, &[]); + canonicalise_then_jump(builder, next_block, (&[], &[])); builder.seal_block(next_block); // Only predecessor is the current block. builder.switch_to_block(next_block); @@ -319,7 +383,7 @@ pub fn translate_operator( if !builder.is_unreachable() || !builder.is_pristine() { let return_count = frame.num_return_values(); - let return_args = state.peekn_mut(return_count); + let return_args = state.peekn(return_count); canonicalise_then_jump(builder, frame.following_code(), return_args); // You might expect that if we just finished an `if` block that // didn't have a corresponding `else` block, then we would clean @@ -376,7 +440,7 @@ pub fn translate_operator( }; (return_count, frame.br_destination()) }; - let destination_args = state.peekn_mut(return_count); + let destination_args = state.peekn(return_count); canonicalise_then_jump(builder, br_destination, destination_args); state.popn(return_count); state.reachable = false; @@ -400,7 +464,7 @@ pub fn translate_operator( min_depth_frame.num_return_values() } }; - let val = state.pop1(); + let (val, _) = state.pop1(); let mut data = JumpTableData::with_capacity(depths.len()); if jump_args_count == 0 { // No jump arguments @@ -457,7 +521,7 @@ pub fn translate_operator( frame.set_branched_to_exit(); frame.br_destination() }; - let destination_args = state.peekn_mut(return_count); + let destination_args = state.peekn(return_count); canonicalise_then_jump(builder, real_dest_block, destination_args); } state.popn(return_count); @@ -472,16 +536,19 @@ pub fn translate_operator( (return_count, frame.br_destination()) }; { - let return_args = state.peekn_mut(return_count); + let (return_args, return_args_metadata) = state.peekn_mut(return_count); + // TODO(reftypes): maybe ref count here? let return_types = wasm_param_types(&builder.func.signature.returns, |i| { environ.is_wasm_return(&builder.func.signature, i) }); bitcast_arguments(return_args, &return_types, builder); match environ.return_mode() { ReturnMode::NormalReturns => builder.ins().return_(return_args), - ReturnMode::FallthroughReturn => { - canonicalise_then_jump(builder, br_destination, return_args) - } + ReturnMode::FallthroughReturn => canonicalise_then_jump( + builder, + br_destination, + (&*return_args, &*return_args_metadata), + ), }; } state.popn(return_count); @@ -495,21 +562,18 @@ pub fn translate_operator( Operator::Call { function_index } => { let (fref, num_args) = state.get_direct_func(builder.func, *function_index, environ)?; + let (args, _args_metadata) = state.peekn_mut(num_args); + // Bitcast any vector arguments to their default type, I8X16, before calling. let callee_signature = &builder.func.dfg.signatures[builder.func.dfg.ext_funcs[fref].signature]; - let args = state.peekn_mut(num_args); let types = wasm_param_types(&callee_signature.params, |i| { environ.is_wasm_parameter(&callee_signature, i) }); bitcast_arguments(args, &types, builder); + let func_index = FunctionIndex::from_u32(*function_index); - let call = environ.translate_call( - builder.cursor(), - FunctionIndex::from_u32(*function_index), - fref, - args, - )?; + let call = environ.translate_call(builder.cursor(), func_index, fref, args)?; let inst_results = builder.inst_results(call); debug_assert_eq!( inst_results.len(), @@ -518,32 +582,47 @@ pub fn translate_operator( .len(), "translate_call results should match the call signature" ); + let func_type = environ.get_function_type(func_index).unwrap(); + let mut results_metadata = Vec::with_capacity(func_type.results().len()); + for result in func_type.results() { + results_metadata.push(if *result == WasmerType::ExternRef { + ValueExtraInfo { + ref_counted: true, + ..Default::default() + } + } else { + Default::default() + }); + } state.popn(num_args); - state.pushn(inst_results); + state.pushn(inst_results, &results_metadata); } Operator::CallIndirect { index, table_index } => { // `index` is the index of the function's signature and `table_index` is the index of // the table to search the function in. let (sigref, num_args) = state.get_indirect_sig(builder.func, *index, environ)?; let table = state.get_or_create_table(builder.func, *table_index, environ)?; - let callee = state.pop1(); + let (callee, _) = state.pop1(); // Bitcast any vector arguments to their default type, I8X16, before calling. let callee_signature = &builder.func.dfg.signatures[sigref]; - let args = state.peekn_mut(num_args); + let (args, _) = state.peekn_mut(num_args); let types = wasm_param_types(&callee_signature.params, |i| { environ.is_wasm_parameter(&callee_signature, i) }); bitcast_arguments(args, &types, builder); + let (args, _args_metadata) = state.peekn(num_args); + let sig_idx = SignatureIndex::from_u32(*index); + let call = environ.translate_call_indirect( builder.cursor(), TableIndex::from_u32(*table_index), table, - SignatureIndex::from_u32(*index), + sig_idx, sigref, callee, - state.peekn(num_args), + args, )?; let inst_results = builder.inst_results(call); debug_assert_eq!( @@ -551,8 +630,20 @@ pub fn translate_operator( builder.func.dfg.signatures[sigref].returns.len(), "translate_call_indirect results should match the call signature" ); + let func_type = environ.get_function_sig(sig_idx).unwrap(); + let mut results_metadata = Vec::with_capacity(func_type.results().len()); + for result in func_type.results() { + results_metadata.push(if *result == WasmerType::ExternRef { + ValueExtraInfo { + ref_counted: true, + ..Default::default() + } + } else { + Default::default() + }); + } state.popn(num_args); - state.pushn(inst_results); + state.pushn(inst_results, &results_metadata); } /******************************* Memory management *********************************** * Memory management is handled by environment. It is usually translated into calls to @@ -563,7 +654,7 @@ pub fn translate_operator( // argument to be a memory index. let heap_index = MemoryIndex::from_u32(*mem); let heap = state.get_heap(builder.func, *mem, environ)?; - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(environ.translate_memory_grow(builder.cursor(), heap_index, heap, val)?) } Operator::MemorySize { mem, mem_byte: _ } => { @@ -683,246 +774,246 @@ pub fn translate_operator( } /******************************* Unary Operators *************************************/ Operator::I32Clz | Operator::I64Clz => { - let arg = state.pop1(); + let (arg, _) = state.pop1(); state.push1(builder.ins().clz(arg)); } Operator::I32Ctz | Operator::I64Ctz => { - let arg = state.pop1(); + let (arg, _) = state.pop1(); state.push1(builder.ins().ctz(arg)); } Operator::I32Popcnt | Operator::I64Popcnt => { - let arg = state.pop1(); + let (arg, _) = state.pop1(); state.push1(builder.ins().popcnt(arg)); } Operator::I64ExtendI32S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().sextend(I64, val)); } Operator::I64ExtendI32U => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().uextend(I64, val)); } Operator::I32WrapI64 => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().ireduce(I32, val)); } Operator::F32Sqrt | Operator::F64Sqrt => { - let arg = state.pop1(); + let (arg, _) = state.pop1(); state.push1(builder.ins().sqrt(arg)); } Operator::F32Ceil | Operator::F64Ceil => { - let arg = state.pop1(); + let (arg, _) = state.pop1(); state.push1(builder.ins().ceil(arg)); } Operator::F32Floor | Operator::F64Floor => { - let arg = state.pop1(); + let (arg, _) = state.pop1(); state.push1(builder.ins().floor(arg)); } Operator::F32Trunc | Operator::F64Trunc => { - let arg = state.pop1(); + let (arg, _) = state.pop1(); state.push1(builder.ins().trunc(arg)); } Operator::F32Nearest | Operator::F64Nearest => { - let arg = state.pop1(); + let (arg, _) = state.pop1(); state.push1(builder.ins().nearest(arg)); } Operator::F32Abs | Operator::F64Abs => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fabs(val)); } Operator::F32Neg | Operator::F64Neg => { - let arg = state.pop1(); + let (arg, _) = state.pop1(); state.push1(builder.ins().fneg(arg)); } Operator::F64ConvertI64U | Operator::F64ConvertI32U => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_from_uint(F64, val)); } Operator::F64ConvertI64S | Operator::F64ConvertI32S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_from_sint(F64, val)); } Operator::F32ConvertI64S | Operator::F32ConvertI32S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_from_sint(F32, val)); } Operator::F32ConvertI64U | Operator::F32ConvertI32U => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_from_uint(F32, val)); } Operator::F64PromoteF32 => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fpromote(F64, val)); } Operator::F32DemoteF64 => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fdemote(F32, val)); } Operator::I64TruncF64S | Operator::I64TruncF32S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_to_sint(I64, val)); } Operator::I32TruncF64S | Operator::I32TruncF32S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_to_sint(I32, val)); } Operator::I64TruncF64U | Operator::I64TruncF32U => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_to_uint(I64, val)); } Operator::I32TruncF64U | Operator::I32TruncF32U => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_to_uint(I32, val)); } Operator::I64TruncSatF64S | Operator::I64TruncSatF32S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_to_sint_sat(I64, val)); } Operator::I32TruncSatF64S | Operator::I32TruncSatF32S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_to_sint_sat(I32, val)); } Operator::I64TruncSatF64U | Operator::I64TruncSatF32U => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_to_uint_sat(I64, val)); } Operator::I32TruncSatF64U | Operator::I32TruncSatF32U => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().fcvt_to_uint_sat(I32, val)); } Operator::F32ReinterpretI32 => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().bitcast(F32, val)); } Operator::F64ReinterpretI64 => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().bitcast(F64, val)); } Operator::I32ReinterpretF32 => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().bitcast(I32, val)); } Operator::I64ReinterpretF64 => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().bitcast(I64, val)); } Operator::I32Extend8S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().ireduce(I8, val)); - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().sextend(I32, val)); } Operator::I32Extend16S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().ireduce(I16, val)); - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().sextend(I32, val)); } Operator::I64Extend8S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().ireduce(I8, val)); - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().sextend(I64, val)); } Operator::I64Extend16S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().ireduce(I16, val)); - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().sextend(I64, val)); } Operator::I64Extend32S => { - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().ireduce(I32, val)); - let val = state.pop1(); + let (val, _) = state.pop1(); state.push1(builder.ins().sextend(I64, val)); } /****************************** Binary Operators ************************************/ Operator::I32Add | Operator::I64Add => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().iadd(arg1, arg2)); } Operator::I32And | Operator::I64And => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().band(arg1, arg2)); } Operator::I32Or | Operator::I64Or => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().bor(arg1, arg2)); } Operator::I32Xor | Operator::I64Xor => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().bxor(arg1, arg2)); } Operator::I32Shl | Operator::I64Shl => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().ishl(arg1, arg2)); } Operator::I32ShrS | Operator::I64ShrS => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().sshr(arg1, arg2)); } Operator::I32ShrU | Operator::I64ShrU => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().ushr(arg1, arg2)); } Operator::I32Rotl | Operator::I64Rotl => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().rotl(arg1, arg2)); } Operator::I32Rotr | Operator::I64Rotr => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().rotr(arg1, arg2)); } Operator::F32Add | Operator::F64Add => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().fadd(arg1, arg2)); } Operator::I32Sub | Operator::I64Sub => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().isub(arg1, arg2)); } Operator::F32Sub | Operator::F64Sub => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().fsub(arg1, arg2)); } Operator::I32Mul | Operator::I64Mul => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().imul(arg1, arg2)); } Operator::F32Mul | Operator::F64Mul => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().fmul(arg1, arg2)); } Operator::F32Div | Operator::F64Div => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().fdiv(arg1, arg2)); } Operator::I32DivS | Operator::I64DivS => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().sdiv(arg1, arg2)); } Operator::I32DivU | Operator::I64DivU => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().udiv(arg1, arg2)); } Operator::I32RemS | Operator::I64RemS => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().srem(arg1, arg2)); } Operator::I32RemU | Operator::I64RemU => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().urem(arg1, arg2)); } Operator::F32Min | Operator::F64Min => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().fmin(arg1, arg2)); } Operator::F32Max | Operator::F64Max => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().fmax(arg1, arg2)); } Operator::F32Copysign | Operator::F64Copysign => { - let (arg1, arg2) = state.pop2(); + let ((arg1, _), (arg2, _)) = state.pop2(); state.push1(builder.ins().fcopysign(arg1, arg2)); } /**************************** Comparison Operators **********************************/ @@ -951,7 +1042,7 @@ pub fn translate_operator( translate_icmp(IntCC::UnsignedGreaterThanOrEqual, builder, state) } Operator::I32Eqz | Operator::I64Eqz => { - let arg = state.pop1(); + let (arg, _) = state.pop1(); let val = builder.ins().icmp_imm(IntCC::Equal, arg, 0); state.push1(builder.ins().bint(I32, val)); } @@ -969,7 +1060,7 @@ pub fn translate_operator( } Operator::RefNull { ty } => state.push1(environ.translate_ref_null(builder.cursor(), *ty)?), Operator::RefIsNull => { - let value = state.pop1(); + let (value, _) = state.pop1(); state.push1(environ.translate_ref_is_null(builder.cursor(), value)?); } Operator::RefFunc { function_index } => { @@ -987,9 +1078,9 @@ pub fn translate_operator( }; let heap_index = MemoryIndex::from_u32(memarg.memory); let heap = state.get_heap(builder.func, memarg.memory, environ)?; - let timeout = state.pop1(); // 64 (fixed) - let expected = state.pop1(); // 32 or 64 (per the `Ixx` in `IxxAtomicWait`) - let addr = state.pop1(); // 32 (fixed) + let (timeout, _) = state.pop1(); // 64 (fixed) + let (expected, _) = state.pop1(); // 32 or 64 (per the `Ixx` in `IxxAtomicWait`) + let (addr, _) = state.pop1(); // 32 (fixed) assert!(builder.func.dfg.value_type(expected) == implied_ty); // `fn translate_atomic_wait` can inspect the type of `expected` to figure out what // code it needs to generate, if it wants. @@ -1006,8 +1097,8 @@ pub fn translate_operator( Operator::MemoryAtomicNotify { memarg } => { let heap_index = MemoryIndex::from_u32(memarg.memory); let heap = state.get_heap(builder.func, memarg.memory, environ)?; - let count = state.pop1(); // 32 (fixed) - let addr = state.pop1(); // 32 (fixed) + let (count, _) = state.pop1(); // 32 (fixed) + let (addr, _) = state.pop1(); // 32 (fixed) let res = environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count)?; state.push1(res); @@ -1218,9 +1309,9 @@ pub fn translate_operator( let dst_index = MemoryIndex::from_u32(*dst); let src_heap = state.get_heap(builder.func, *src, environ)?; let dst_heap = state.get_heap(builder.func, *dst, environ)?; - let len = state.pop1(); - let src_pos = state.pop1(); - let dst_pos = state.pop1(); + let (len, _) = state.pop1(); + let (src_pos, _) = state.pop1(); + let (dst_pos, _) = state.pop1(); environ.translate_memory_copy( builder.cursor(), src_index, @@ -1235,17 +1326,17 @@ pub fn translate_operator( Operator::MemoryFill { mem } => { let heap_index = MemoryIndex::from_u32(*mem); let heap = state.get_heap(builder.func, *mem, environ)?; - let len = state.pop1(); - let val = state.pop1(); - let dest = state.pop1(); + let (len, _) = state.pop1(); + let (val, _) = state.pop1(); + let (dest, _) = state.pop1(); environ.translate_memory_fill(builder.cursor(), heap_index, heap, dest, val, len)?; } Operator::MemoryInit { segment, mem } => { let heap_index = MemoryIndex::from_u32(*mem); let heap = state.get_heap(builder.func, *mem, environ)?; - let len = state.pop1(); - let src = state.pop1(); - let dest = state.pop1(); + let (len, _) = state.pop1(); + let (src, _) = state.pop1(); + let (dest, _) = state.pop1(); environ.translate_memory_init( builder.cursor(), heap_index, @@ -1270,8 +1361,8 @@ pub fn translate_operator( Operator::TableGrow { table: index } => { let table_index = TableIndex::from_u32(*index); let table = state.get_or_create_table(builder.func, *index, environ)?; - let delta = state.pop1(); - let init_value = state.pop1(); + let (delta, _) = state.pop1(); + let (init_value, _) = state.pop1(); state.push1(environ.translate_table_grow( builder.cursor(), table_index, @@ -1283,14 +1374,16 @@ pub fn translate_operator( Operator::TableGet { table: index } => { let table_index = TableIndex::from_u32(*index); let table = state.get_or_create_table(builder.func, *index, environ)?; - let index = state.pop1(); + let (index, _) = state.pop1(); state.push1(environ.translate_table_get(builder, table_index, table, index)?); } Operator::TableSet { table: index } => { let table_index = TableIndex::from_u32(*index); let table = state.get_or_create_table(builder.func, *index, environ)?; - let value = state.pop1(); - let index = state.pop1(); + // We don't touch the ref count here because we're passing it to the host + // then dropping it from the stack. Thus 1 + -1 = 0. + let (value, _) = state.pop1(); + let (index, _) = state.pop1(); environ.translate_table_set(builder, table_index, table, value, index)?; } Operator::TableCopy { @@ -1299,9 +1392,9 @@ pub fn translate_operator( } => { let dst_table = state.get_or_create_table(builder.func, *dst_table_index, environ)?; let src_table = state.get_or_create_table(builder.func, *src_table_index, environ)?; - let len = state.pop1(); - let src = state.pop1(); - let dest = state.pop1(); + let (len, _) = state.pop1(); + let (src, _) = state.pop1(); + let (dest, _) = state.pop1(); environ.translate_table_copy( builder.cursor(), TableIndex::from_u32(*dst_table_index), @@ -1315,9 +1408,9 @@ pub fn translate_operator( } Operator::TableFill { table } => { let table_index = TableIndex::from_u32(*table); - let len = state.pop1(); - let val = state.pop1(); - let dest = state.pop1(); + let (len, _) = state.pop1(); + let (val, _) = state.pop1(); + let (dest, _) = state.pop1(); environ.translate_table_fill(builder.cursor(), table_index, dest, val, len)?; } Operator::TableInit { @@ -1325,9 +1418,9 @@ pub fn translate_operator( table: table_index, } => { let table = state.get_or_create_table(builder.func, *table_index, environ)?; - let len = state.pop1(); - let src = state.pop1(); - let dest = state.pop1(); + let (len, _) = state.pop1(); + let (src, _) = state.pop1(); + let (dest, _) = state.pop1(); environ.translate_table_init( builder.cursor(), *segment, @@ -1350,7 +1443,9 @@ pub fn translate_operator( state.push1(value) } Operator::I8x16Splat | Operator::I16x8Splat => { - let reduced = builder.ins().ireduce(type_of(op).lane_type(), state.pop1()); + let reduced = builder + .ins() + .ireduce(type_of(op).lane_type(), state.pop1().0); let splatted = builder.ins().splat(type_of(op), reduced); state.push1(splatted) } @@ -1358,7 +1453,7 @@ pub fn translate_operator( | Operator::I64x2Splat | Operator::F32x4Splat | Operator::F64x2Splat => { - let splatted = builder.ins().splat(type_of(op), state.pop1()); + let splatted = builder.ins().splat(type_of(op), state.pop1().0); state.push1(splatted) } Operator::V128Load8Splat { memarg } @@ -1375,7 +1470,7 @@ pub fn translate_operator( state, environ, )?; - let splatted = builder.ins().splat(type_of(op), state.pop1()); + let splatted = builder.ins().splat(type_of(op), state.pop1().0); state.push1(splatted) // let opcode = ir::Opcode::LoadSplat; @@ -1411,7 +1506,7 @@ pub fn translate_operator( state.push1(builder.ins().extractlane(vector, lane.clone())) } Operator::I8x16ReplaceLane { lane } | Operator::I16x8ReplaceLane { lane } => { - let (vector, replacement) = state.pop2(); + let ((vector, _), (replacement, _)) = state.pop2(); let ty = type_of(op); let reduced = builder.ins().ireduce(ty.lane_type(), replacement); let vector = optionally_bitcast_vector(vector, ty, builder); @@ -1421,7 +1516,7 @@ pub fn translate_operator( | Operator::I64x2ReplaceLane { lane } | Operator::F32x4ReplaceLane { lane } | Operator::F64x2ReplaceLane { lane } => { - let (vector, replacement) = state.pop2(); + let ((vector, _), (replacement, _)) = state.pop2(); let vector = optionally_bitcast_vector(vector, type_of(op), builder); state.push1(builder.ins().insertlane(vector, replacement, *lane)) } @@ -1513,11 +1608,11 @@ pub fn translate_operator( state.push1(builder.ins().band_not(a, b)) } Operator::V128Not => { - let a = state.pop1(); + let (a, _) = state.pop1(); state.push1(builder.ins().bnot(a)); } Operator::I8x16Shl | Operator::I16x8Shl | Operator::I32x4Shl | Operator::I64x2Shl => { - let (a, b) = state.pop2(); + let ((a, _), (b, _)) = state.pop2(); let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder); let bitwidth = i64::from(type_of(op).lane_bits()); // The spec expects to shift with `b mod lanewidth`; so, e.g., for 16 bit lane-width @@ -1526,7 +1621,7 @@ pub fn translate_operator( state.push1(builder.ins().ishl(bitcast_a, b_mod_bitwidth)) } Operator::I8x16ShrU | Operator::I16x8ShrU | Operator::I32x4ShrU | Operator::I64x2ShrU => { - let (a, b) = state.pop2(); + let ((a, _), (b, _)) = state.pop2(); let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder); let bitwidth = i64::from(type_of(op).lane_bits()); // The spec expects to shift with `b mod lanewidth`; so, e.g., for 16 bit lane-width @@ -1535,7 +1630,7 @@ pub fn translate_operator( state.push1(builder.ins().ushr(bitcast_a, b_mod_bitwidth)) } Operator::I8x16ShrS | Operator::I16x8ShrS | Operator::I32x4ShrS | Operator::I64x2ShrS => { - let (a, b) = state.pop2(); + let ((a, _), (b, _)) = state.pop2(); let bitcast_a = optionally_bitcast_vector(a, type_of(op), builder); let bitwidth = i64::from(type_of(op).lane_bits()); // The spec expects to shift with `b mod lanewidth`; so, e.g., for 16 bit lane-width @@ -1544,7 +1639,7 @@ pub fn translate_operator( state.push1(builder.ins().sshr(bitcast_a, b_mod_bitwidth)) } Operator::V128Bitselect => { - let (a, b, c) = state.pop3(); + let ((a, _), (b, _), (c, _)) = state.pop3(); let bitcast_a = optionally_bitcast_vector(a, I8X16, builder); let bitcast_b = optionally_bitcast_vector(b, I8X16, builder); let bitcast_c = optionally_bitcast_vector(c, I8X16, builder); @@ -2030,7 +2125,7 @@ fn prepare_load( state: &mut FuncTranslationState, environ: &mut FE, ) -> WasmResult<(MemFlags, Value, Offset32)> { - let addr32 = state.pop1(); + let (addr32, _) = state.pop1(); let heap = state.get_heap(builder.func, memarg.memory, environ)?; let (base, offset) = get_heap_addr( @@ -2079,7 +2174,7 @@ fn translate_store( state: &mut FuncTranslationState, environ: &mut FE, ) -> WasmResult<()> { - let (addr32, val) = state.pop2(); + let ((addr32, _), (val, _)) = state.pop2(); let val_ty = builder.func.dfg.value_type(val); let heap = state.get_heap(builder.func, memarg.memory, environ)?; @@ -2110,7 +2205,7 @@ fn mem_op_size(opcode: ir::Opcode, ty: Type) -> u32 { } fn translate_icmp(cc: IntCC, builder: &mut FunctionBuilder, state: &mut FuncTranslationState) { - let (arg0, arg1) = state.pop2(); + let ((arg0, _), (arg1, _)) = state.pop2(); let val = builder.ins().icmp(cc, arg0, arg1); state.push1(builder.ins().bint(I32, val)); } @@ -2167,7 +2262,7 @@ fn translate_atomic_rmw( state: &mut FuncTranslationState, environ: &mut FE, ) -> WasmResult<()> { - let (linear_mem_addr, mut arg2) = state.pop2(); + let ((linear_mem_addr, _), (mut arg2, _)) = state.pop2(); let arg2_ty = builder.func.dfg.value_type(arg2); // The operation is performed at type `access_ty`, and the old value is zero-extended @@ -2215,7 +2310,7 @@ fn translate_atomic_cas( state: &mut FuncTranslationState, environ: &mut FE, ) -> WasmResult<()> { - let (linear_mem_addr, mut expected, mut replacement) = state.pop3(); + let ((linear_mem_addr, _), (mut expected, _), (mut replacement, _)) = state.pop3(); let expected_ty = builder.func.dfg.value_type(expected); let replacement_ty = builder.func.dfg.value_type(replacement); @@ -2268,7 +2363,7 @@ fn translate_atomic_load( state: &mut FuncTranslationState, environ: &mut FE, ) -> WasmResult<()> { - let linear_mem_addr = state.pop1(); + let (linear_mem_addr, _) = state.pop1(); // The load is performed at type `access_ty`, and the loaded value is zero extended // to `widened_ty`. @@ -2309,7 +2404,7 @@ fn translate_atomic_store( state: &mut FuncTranslationState, environ: &mut FE, ) -> WasmResult<()> { - let (linear_mem_addr, mut data) = state.pop2(); + let ((linear_mem_addr, _), (mut data, _)) = state.pop2(); let data_ty = builder.func.dfg.value_type(data); // The operation is performed at type `access_ty`, and the data to be stored may first @@ -2350,14 +2445,14 @@ fn translate_vector_icmp( builder: &mut FunctionBuilder, state: &mut FuncTranslationState, ) { - let (a, b) = state.pop2(); + let ((a, _), (b, _)) = state.pop2(); let bitcast_a = optionally_bitcast_vector(a, needed_type, builder); let bitcast_b = optionally_bitcast_vector(b, needed_type, builder); state.push1(builder.ins().icmp(cc, bitcast_a, bitcast_b)) } fn translate_fcmp(cc: FloatCC, builder: &mut FunctionBuilder, state: &mut FuncTranslationState) { - let (arg0, arg1) = state.pop2(); + let ((arg0, _), (arg1, _)) = state.pop2(); let val = builder.ins().fcmp(cc, arg0, arg1); state.push1(builder.ins().bint(I32, val)); } @@ -2368,7 +2463,7 @@ fn translate_vector_fcmp( builder: &mut FunctionBuilder, state: &mut FuncTranslationState, ) { - let (a, b) = state.pop2(); + let ((a, _), (b, _)) = state.pop2(); let bitcast_a = optionally_bitcast_vector(a, needed_type, builder); let bitcast_b = optionally_bitcast_vector(b, needed_type, builder); state.push1(builder.ins().fcmp(cc, bitcast_a, bitcast_b)) @@ -2379,12 +2474,13 @@ fn translate_br_if( builder: &mut FunctionBuilder, state: &mut FuncTranslationState, ) { - let val = state.pop1(); + let (val, _) = state.pop1(); let (br_destination, inputs) = translate_br_if_args(relative_depth, state); + let inputs = (&*inputs.0, &*inputs.1); canonicalise_then_brnz(builder, val, br_destination, inputs); let next_block = builder.create_block(); - canonicalise_then_jump(builder, next_block, &[]); + canonicalise_then_jump(builder, next_block, (&[], &[])); builder.seal_block(next_block); // The only predecessor is the current block. builder.switch_to_block(next_block); } @@ -2392,7 +2488,7 @@ fn translate_br_if( fn translate_br_if_args( relative_depth: u32, state: &mut FuncTranslationState, -) -> (ir::Block, &mut [ir::Value]) { +) -> (ir::Block, (&mut [ir::Value], &mut [ValueExtraInfo])) { let i = state.control_stack.len() - 1 - (relative_depth as usize); let (return_count, br_destination) = { let frame = &mut state.control_stack[i]; @@ -2653,10 +2749,10 @@ fn canonicalise_v128_values<'a>( fn canonicalise_then_jump( builder: &mut FunctionBuilder, destination: ir::Block, - params: &[ir::Value], + params: (&[ir::Value], &[ValueExtraInfo]), ) -> ir::Inst { let mut tmp_canonicalised = SmallVec::<[ir::Value; 16]>::new(); - let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params); + let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params.0); builder.ins().jump(destination, canonicalised) } @@ -2665,10 +2761,10 @@ fn canonicalise_then_brz( builder: &mut FunctionBuilder, cond: ir::Value, destination: ir::Block, - params: &[Value], + params: (&[ir::Value], &[ValueExtraInfo]), ) -> ir::Inst { let mut tmp_canonicalised = SmallVec::<[ir::Value; 16]>::new(); - let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params); + let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params.0); builder.ins().brz(cond, destination, canonicalised) } @@ -2677,10 +2773,10 @@ fn canonicalise_then_brnz( builder: &mut FunctionBuilder, cond: ir::Value, destination: ir::Block, - params: &[Value], + params: (&[ir::Value], &[ValueExtraInfo]), ) -> ir::Inst { let mut tmp_canonicalised = SmallVec::<[ir::Value; 16]>::new(); - let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params); + let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params.0); builder.ins().brnz(cond, destination, canonicalised) } @@ -2692,7 +2788,7 @@ fn pop1_with_bitcast( needed_type: Type, builder: &mut FunctionBuilder, ) -> Value { - optionally_bitcast_vector(state.pop1(), needed_type, builder) + optionally_bitcast_vector(state.pop1().0, needed_type, builder) } /// A helper for popping and bitcasting two values; since SIMD values can lose their type by @@ -2703,7 +2799,7 @@ fn pop2_with_bitcast( needed_type: Type, builder: &mut FunctionBuilder, ) -> (Value, Value) { - let (a, b) = state.pop2(); + let ((a, _), (b, _)) = state.pop2(); let bitcast_a = optionally_bitcast_vector(a, needed_type, builder); let bitcast_b = optionally_bitcast_vector(b, needed_type, builder); (bitcast_a, bitcast_b) diff --git a/lib/compiler-cranelift/src/translator/func_environ.rs b/lib/compiler-cranelift/src/translator/func_environ.rs index efdcb0699..a5f74a094 100644 --- a/lib/compiler-cranelift/src/translator/func_environ.rs +++ b/lib/compiler-cranelift/src/translator/func_environ.rs @@ -14,7 +14,10 @@ use cranelift_codegen::isa::TargetFrontendConfig; use cranelift_frontend::FunctionBuilder; use wasmer_compiler::wasmparser::{Operator, Type}; use wasmer_compiler::WasmResult; -use wasmer_types::{FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex}; +use wasmer_types::{ + FunctionIndex, FunctionType, GlobalIndex, LocalFunctionIndex, MemoryIndex, SignatureIndex, + TableIndex, Type as WasmerType, +}; /// The value of a WebAssembly global variable. #[derive(Clone, Copy)] @@ -333,6 +336,20 @@ pub trait FuncEnvironment: TargetEnvironment { len: ir::Value, ) -> WasmResult<()>; + /// Translates an externref ref count increment. + fn translate_externref_inc( + &mut self, + pos: cranelift_codegen::cursor::FuncCursor<'_>, + externref: ir::Value, + ) -> WasmResult<()>; + + /// Translates an externref ref count decrement. + fn translate_externref_dec( + &mut self, + pos: cranelift_codegen::cursor::FuncCursor<'_>, + externref: ir::Value, + ) -> WasmResult<()>; + /// Translate a `table.init` WebAssembly instruction. #[allow(clippy::too_many_arguments)] fn translate_table_init( @@ -378,7 +395,7 @@ pub trait FuncEnvironment: TargetEnvironment { value: ir::Value, ) -> WasmResult { let is_null = pos.ins().is_null(value); - Ok(pos.ins().bint(ir::types::I32, is_null)) + Ok(pos.ins().bint(ir::types::I64, is_null)) } /// Translate a `ref.func` WebAssembly instruction. @@ -467,4 +484,28 @@ pub trait FuncEnvironment: TargetEnvironment { ) -> WasmResult<()> { Ok(()) } + + /// Get the type of the global at the given index. + fn get_global_type(&self, global_index: GlobalIndex) -> Option; + + /// Push a local declaration on to the stack to track the type of locals. + fn push_local_decl_on_stack(&mut self, ty: WasmerType); + + /// Push locals for a the params of a function on to the stack. + fn push_params_on_stack(&mut self, function_index: LocalFunctionIndex); + + /// Get the type of the local at the given index. + fn get_local_type(&self, local_index: u32) -> Option; + + /// Get the types of all the current locals. + fn get_local_types(&self) -> &[WasmerType]; + + /// Get the type of the local at the given index. + fn get_function_type(&self, function_index: FunctionIndex) -> Option<&FunctionType>; + + /// Get the type of a function with the given signature index. + fn get_function_sig(&self, sig_index: SignatureIndex) -> Option<&FunctionType>; + + /// Drops all locals that need to be dropped. Useful for returning from functions. + fn translate_drop_locals(&mut self, builder: &mut FunctionBuilder) -> WasmResult<()>; } diff --git a/lib/compiler-cranelift/src/translator/func_state.rs b/lib/compiler-cranelift/src/translator/func_state.rs index 1cd8db9bd..6255b9e32 100644 --- a/lib/compiler-cranelift/src/translator/func_state.rs +++ b/lib/compiler-cranelift/src/translator/func_state.rs @@ -213,6 +213,13 @@ impl ControlStackFrame { } } +/// Extra info about values. For example, on the stack. +#[derive(Debug, Clone, Default)] +pub struct ValueExtraInfo { + /// Whether or not the value should be ref counted. + pub ref_counted: bool, +} + /// Contains information passed along during a function's translation and that records: /// /// - The current value and control stacks. @@ -263,6 +270,8 @@ impl FuncTranslationState { pub(crate) fn new() -> Self { Self { stack: Vec::new(), + // TODO(reftypes): + //metadata_stack: Vec::new(), control_stack: Vec::new(), reachable: true, globals: HashMap::new(), @@ -300,43 +309,66 @@ impl FuncTranslationState { ); } - /// Push a value. + /// Push a value with extra info attached. + pub(crate) fn push1_extra(&mut self, val: (Value, ValueExtraInfo)) { + self.stack.push(val.0); + // TODO(reftypes): + //self.metadata_stack.push(val.1); + } + + /// Push a value with default extra info. pub(crate) fn push1(&mut self, val: Value) { self.stack.push(val); + // TODO(reftypes): + //self.metadata_stack.push(ValueExtraInfo::default()); } /// Push multiple values. - pub(crate) fn pushn(&mut self, vals: &[Value]) { + pub(crate) fn pushn(&mut self, vals: &[Value], _vals_metadata: &[ValueExtraInfo]) { + assert_eq!(vals.len(), _vals_metadata.len()); self.stack.extend_from_slice(vals); + // TODO(reftypes): + //self.metadata_stack.extend_from_slice(vals_metadata); } /// Pop one value. - pub(crate) fn pop1(&mut self) -> Value { - self.stack + pub(crate) fn pop1(&mut self) -> (Value, ValueExtraInfo) { + let val = self + .stack .pop() - .expect("attempted to pop a value from an empty stack") + .expect("attempted to pop a value from an empty stack"); + let val_metadata = Default::default(); + (val, val_metadata) } /// Peek at the top of the stack without popping it. - pub(crate) fn peek1(&self) -> Value { - *self + pub(crate) fn peek1(&self) -> (Value, ValueExtraInfo) { + let val = *self .stack .last() - .expect("attempted to peek at a value on an empty stack") + .expect("attempted to peek at a value on an empty stack"); + let val_metadata = Default::default(); + (val, val_metadata) } /// Pop two values. Return them in the order they were pushed. - pub(crate) fn pop2(&mut self) -> (Value, Value) { - let v2 = self.stack.pop().unwrap(); - let v1 = self.stack.pop().unwrap(); + pub(crate) fn pop2(&mut self) -> ((Value, ValueExtraInfo), (Value, ValueExtraInfo)) { + let v2 = self.pop1(); + let v1 = self.pop1(); (v1, v2) } /// Pop three values. Return them in the order they were pushed. - pub(crate) fn pop3(&mut self) -> (Value, Value, Value) { - let v3 = self.stack.pop().unwrap(); - let v2 = self.stack.pop().unwrap(); - let v1 = self.stack.pop().unwrap(); + pub(crate) fn pop3( + &mut self, + ) -> ( + (Value, ValueExtraInfo), + (Value, ValueExtraInfo), + (Value, ValueExtraInfo), + ) { + let v3 = self.pop1(); + let v2 = self.pop1(); + let v1 = self.pop1(); (v1, v2, v3) } @@ -349,7 +381,14 @@ impl FuncTranslationState { "attempted to access {} values but stack only has {} values", n, self.stack.len() - ) + ); + // TODO(reftypes): + /*debug_assert!( + n <= self.metadata_stack.len(), + "attempted to access {} values but stack only has {} values", + n, + self.metadata_stack.len() + );*/ } /// Pop the top `n` values on the stack. @@ -362,16 +401,25 @@ impl FuncTranslationState { } /// Peek at the top `n` values on the stack in the order they were pushed. - pub(crate) fn peekn(&self, n: usize) -> &[Value] { + pub(crate) fn peekn(&self, n: usize) -> (&[Value], &[ValueExtraInfo]) { self.ensure_length_is_at_least(n); - &self.stack[self.stack.len() - n..] + let vals = &self.stack[self.stack.len() - n..]; + // TODO(reftypes): + let vals_metadata = &[]; //&self.metadata_stack[self.metadata_stack.len() - n..]; + (vals, vals_metadata) } /// Peek at the top `n` values on the stack in the order they were pushed. - pub(crate) fn peekn_mut(&mut self, n: usize) -> &mut [Value] { + pub(crate) fn peekn_mut(&mut self, n: usize) -> (&mut [Value], &mut [ValueExtraInfo]) { self.ensure_length_is_at_least(n); let len = self.stack.len(); - &mut self.stack[len - n..] + // TODO(reftypes): + //let metadata_len = self.metadata_stack.len(); + //assert_eq!(len, metadata_len); + let vals = &mut self.stack[len - n..]; + // TODO(reftypes): + let vals_metadata = &mut []; //&mut self.metadata_stack[metadata_len - n..]; + (vals, vals_metadata) } /// Push a block on the control stack. diff --git a/lib/compiler-cranelift/src/translator/func_translator.rs b/lib/compiler-cranelift/src/translator/func_translator.rs index 84482fec2..20be43ba0 100644 --- a/lib/compiler-cranelift/src/translator/func_translator.rs +++ b/lib/compiler-cranelift/src/translator/func_translator.rs @@ -19,8 +19,8 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; use tracing::info; use wasmer_compiler::wasmparser; use wasmer_compiler::{ - wasm_unsupported, MiddlewareBinaryReader, ModuleMiddlewareChain, ModuleTranslationState, - WasmResult, + wasm_unsupported, wptype_to_type, MiddlewareBinaryReader, ModuleMiddlewareChain, + ModuleTranslationState, WasmResult, }; use wasmer_types::LocalFunctionIndex; @@ -77,6 +77,7 @@ impl FuncTranslator { .middlewares .generate_function_middleware_chain(local_function_index), ); + environ.push_params_on_stack(local_function_index); self.translate_from_reader(module_translation_state, reader, func, environ) } @@ -211,12 +212,14 @@ fn declare_locals( ty => return Err(wasm_unsupported!("unsupported local type {:?}", ty)), }; + let wasmer_ty = wptype_to_type(wasm_type).unwrap(); let ty = builder.func.dfg.value_type(zeroval); for _ in 0..count { let local = Variable::new(*next_local); builder.declare_var(local, ty); builder.def_var(local, zeroval); builder.set_val_label(zeroval, ValueLabel::new(*next_local)); + environ.push_local_decl_on_stack(wasmer_ty); *next_local += 1; } Ok(()) @@ -245,6 +248,8 @@ fn parse_function_body( environ.after_translate_operator(&op, builder, state)?; } + // When returning we drop all values in locals and on the stack. + // The final `End` operator left us in the exit block where we need to manually add a return // instruction. // @@ -253,6 +258,23 @@ fn parse_function_body( if state.reachable { debug_assert!(builder.is_pristine()); if !builder.is_unreachable() { + environ.translate_drop_locals(builder)?; + + let _num_elems_to_drop = state.stack.len() - builder.func.signature.returns.len(); + // drop elements on the stack that we're not returning + /*for val in state + .stack + .iter() + .zip(state.metadata_stack.iter()) + .take(num_elems_to_drop) + .filter(|(_, metadata)| metadata.ref_counted) + .map(|(val, _)| val) + { + environ.translate_externref_dec(builder.cursor(), *val)?; + }*/ + + // TODO: look into what `state.reachable` check above does as well as `!builder.is_unreachable`, do we need that too for ref counting? + match environ.return_mode() { ReturnMode::NormalReturns => { let return_types = wasm_param_types(&builder.func.signature.returns, |i| { @@ -269,6 +291,7 @@ fn parse_function_body( // Discard any remaining values on the stack. Either we just returned them, // or the end of the function is unreachable. state.stack.clear(); + //state.metadata_stack.clear(); debug_assert!(reader.eof()); diff --git a/lib/compiler-llvm/src/abi/aarch64_systemv.rs b/lib/compiler-llvm/src/abi/aarch64_systemv.rs index 4f6ac76a8..dc54a5412 100644 --- a/lib/compiler-llvm/src/abi/aarch64_systemv.rs +++ b/lib/compiler-llvm/src/abi/aarch64_systemv.rs @@ -140,8 +140,7 @@ impl Abi for Aarch64SystemV { Type::I32 | Type::F32 => 32, Type::I64 | Type::F64 => 64, Type::V128 => 128, - Type::ExternRef => unimplemented!("externref in the llvm backend"), - Type::FuncRef => unimplemented!("funcref in the llvm backend"), + Type::ExternRef | Type::FuncRef => 64, /* pointer */ }) .collect::>(); match sig_returns_bitwidths.as_slice() { @@ -285,8 +284,10 @@ impl Abi for Aarch64SystemV { assert!(value.get_type() == intrinsics.i128_ty.as_basic_type_enum()); value } - Type::ExternRef => unimplemented!("externref in the llvm backend"), - Type::FuncRef => unimplemented!("funcref in the llvm backend"), + Type::ExternRef | Type::FuncRef => { + assert!(value.get_type() == intrinsics.funcref_ty.as_basic_type_enum()); + value + } } }; @@ -322,8 +323,7 @@ impl Abi for Aarch64SystemV { Type::I32 | Type::F32 => 32, Type::I64 | Type::F64 => 64, Type::V128 => 128, - Type::ExternRef => unimplemented!("externref in the llvm backend"), - Type::FuncRef => unimplemented!("funcref in the llvm backend"), + Type::ExternRef | Type::FuncRef => 64, /* pointer */ }) .collect::>(); @@ -420,15 +420,12 @@ impl Abi for Aarch64SystemV { .results() .iter() .map(|ty| match ty { - Type::I32 | Type::F32 => Ok(32), - Type::I64 | Type::F64 => Ok(64), - Type::V128 => Ok(128), - ty => Err(CompileError::Codegen(format!( - "is_sret: unimplemented wasmer_types type {:?}", - ty - ))), + Type::I32 | Type::F32 => 32, + Type::I64 | Type::F64 => 64, + Type::V128 => 128, + Type::ExternRef | Type::FuncRef => 64, /* pointer */ }) - .collect::, _>>()?; + .collect::>(); Ok(!matches!( func_sig_returns_bitwidths.as_slice(), diff --git a/lib/compiler-llvm/src/abi/x86_64_systemv.rs b/lib/compiler-llvm/src/abi/x86_64_systemv.rs index 84691d5d2..abf48b8ce 100644 --- a/lib/compiler-llvm/src/abi/x86_64_systemv.rs +++ b/lib/compiler-llvm/src/abi/x86_64_systemv.rs @@ -92,8 +92,7 @@ impl Abi for X86_64SystemV { Type::I32 | Type::F32 => 32, Type::I64 | Type::F64 => 64, Type::V128 => 128, - Type::ExternRef => unimplemented!("externref in the llvm backend"), - Type::FuncRef => unimplemented!("funcref in the llvm backend"), + Type::ExternRef | Type::FuncRef => 64, /* pointer */ }) .collect::>(); @@ -316,26 +315,7 @@ impl Abi for X86_64SystemV { ); builder.build_bitcast(value, intrinsics.f32_ty, "") } - Type::I64 => { - assert!( - value.get_type() == intrinsics.i64_ty.as_basic_type_enum() - || value.get_type() == intrinsics.f64_ty.as_basic_type_enum() - ); - builder.build_bitcast(value, intrinsics.i64_ty, "") - } - Type::F64 => { - assert!( - value.get_type() == intrinsics.i64_ty.as_basic_type_enum() - || value.get_type() == intrinsics.f64_ty.as_basic_type_enum() - ); - builder.build_bitcast(value, intrinsics.f64_ty, "") - } - Type::V128 => { - assert!(value.get_type() == intrinsics.i128_ty.as_basic_type_enum()); - value - } - Type::ExternRef => unimplemented!("externref in the llvm backend"), - Type::FuncRef => unimplemented!("funcref in the llvm backend"), + _ => panic!("should only be called to repack 32-bit values"), } }; @@ -365,8 +345,7 @@ impl Abi for X86_64SystemV { Type::I32 | Type::F32 => 32, Type::I64 | Type::F64 => 64, Type::V128 => 128, - Type::ExternRef => unimplemented!("externref in the llvm backend"), - Type::FuncRef => unimplemented!("funcref in the llvm backend"), + Type::ExternRef | Type::FuncRef => 64, /* pointer */ }) .collect::>(); @@ -475,15 +454,12 @@ impl Abi for X86_64SystemV { .results() .iter() .map(|ty| match ty { - Type::I32 | Type::F32 => Ok(32), - Type::I64 | Type::F64 => Ok(64), - Type::V128 => Ok(128), - ty => Err(CompileError::Codegen(format!( - "is_sret: unimplemented wasmer_types type {:?}", - ty - ))), + Type::I32 | Type::F32 => 32, + Type::I64 | Type::F64 => 64, + Type::V128 => 128, + Type::ExternRef | Type::FuncRef => 64, /* pointer */ }) - .collect::, _>>()?; + .collect::>(); Ok(!matches!( func_sig_returns_bitwidths.as_slice(), diff --git a/lib/compiler-llvm/src/compiler.rs b/lib/compiler-llvm/src/compiler.rs index d3d1b8428..a62755974 100644 --- a/lib/compiler-llvm/src/compiler.rs +++ b/lib/compiler-llvm/src/compiler.rs @@ -57,10 +57,7 @@ impl SymbolRegistry for ShortNames { return None; } let (ty, idx) = name.split_at(1); - let idx = match idx.parse::() { - Ok(v) => v, - Err(_) => return None, - }; + let idx = idx.parse::().ok()?; match ty.chars().next().unwrap() { 'f' => Some(Symbol::LocalFunction(LocalFunctionIndex::from_u32(idx))), 's' => Some(Symbol::Section(SectionIndex::from_u32(idx))), diff --git a/lib/compiler-llvm/src/object_file.rs b/lib/compiler-llvm/src/object_file.rs index 1c118203a..aaed70fa0 100644 --- a/lib/compiler-llvm/src/object_file.rs +++ b/lib/compiler-llvm/src/object_file.rs @@ -54,16 +54,66 @@ where { // TODO: use perfect hash function? let mut libcalls = HashMap::new(); - libcalls.insert("wasmer_raise_trap".to_string(), LibCall::RaiseTrap); - libcalls.insert("truncf".to_string(), LibCall::TruncF32); - libcalls.insert("trunc".to_string(), LibCall::TruncF64); libcalls.insert("ceilf".to_string(), LibCall::CeilF32); libcalls.insert("ceil".to_string(), LibCall::CeilF64); libcalls.insert("floorf".to_string(), LibCall::FloorF32); libcalls.insert("floor".to_string(), LibCall::FloorF64); libcalls.insert("nearbyintf".to_string(), LibCall::NearestF32); libcalls.insert("nearbyint".to_string(), LibCall::NearestF64); - libcalls.insert("wasmer_probestack".to_string(), LibCall::Probestack); + libcalls.insert("truncf".to_string(), LibCall::TruncF32); + libcalls.insert("trunc".to_string(), LibCall::TruncF64); + libcalls.insert("wasmer_vm_f32_ceil".to_string(), LibCall::CeilF32); + libcalls.insert("wasmer_vm_f64_ceil".to_string(), LibCall::CeilF64); + libcalls.insert("wasmer_vm_f32_floor".to_string(), LibCall::FloorF32); + libcalls.insert("wasmer_vm_f64_floor".to_string(), LibCall::FloorF64); + libcalls.insert("wasmer_vm_f32_nearest".to_string(), LibCall::NearestF32); + libcalls.insert("wasmer_vm_f64_nearest".to_string(), LibCall::NearestF64); + libcalls.insert("wasmer_vm_f32_trunc".to_string(), LibCall::TruncF32); + libcalls.insert("wasmer_vm_f64_trunc".to_string(), LibCall::TruncF64); + libcalls.insert("wasmer_vm_memory32_size".to_string(), LibCall::Memory32Size); + libcalls.insert( + "wasmer_vm_imported_memory32_size".to_string(), + LibCall::ImportedMemory32Size, + ); + libcalls.insert("wasmer_vm_table_copy".to_string(), LibCall::TableCopy); + libcalls.insert("wasmer_vm_table_init".to_string(), LibCall::TableInit); + libcalls.insert("wasmer_vm_table_fill".to_string(), LibCall::TableFill); + libcalls.insert("wasmer_vm_table_size".to_string(), LibCall::TableSize); + libcalls.insert( + "wasmer_vm_imported_table_size".to_string(), + LibCall::ImportedTableSize, + ); + libcalls.insert("wasmer_vm_table_get".to_string(), LibCall::TableGet); + libcalls.insert( + "wasmer_vm_imported_table_get".to_string(), + LibCall::ImportedTableGet, + ); + libcalls.insert("wasmer_vm_table_set".to_string(), LibCall::TableSet); + libcalls.insert( + "wasmer_vm_imported_table_set".to_string(), + LibCall::ImportedTableSet, + ); + libcalls.insert("wasmer_vm_table_grow".to_string(), LibCall::TableGrow); + libcalls.insert( + "wasmer_vm_imported_table_grow".to_string(), + LibCall::ImportedTableGrow, + ); + libcalls.insert("wasmer_vm_func_ref".to_string(), LibCall::FuncRef); + libcalls.insert("wasmer_vm_elem_drop".to_string(), LibCall::ElemDrop); + libcalls.insert("wasmer_vm_memory32_copy".to_string(), LibCall::Memory32Copy); + libcalls.insert( + "wasmer_vm_imported_memory32_copy".to_string(), + LibCall::ImportedMemory32Copy, + ); + libcalls.insert("wasmer_vm_memory32_fill".to_string(), LibCall::Memory32Fill); + libcalls.insert( + "wasmer_vm_imported_memory32_fill".to_string(), + LibCall::ImportedMemory32Fill, + ); + libcalls.insert("wasmer_vm_memory32_init".to_string(), LibCall::Memory32Init); + libcalls.insert("wasmer_vm_data_drop".to_string(), LibCall::DataDrop); + libcalls.insert("wasmer_vm_raise_trap".to_string(), LibCall::RaiseTrap); + libcalls.insert("wasmer_vm_probestack".to_string(), LibCall::Probestack); let elf = goblin::elf::Elf::parse(&contents).map_err(map_goblin_err)?; let get_section_name = |section: &goblin::elf::section_header::SectionHeader| { diff --git a/lib/compiler-llvm/src/translator/code.rs b/lib/compiler-llvm/src/translator/code.rs index 6a668c347..0d02e0122 100644 --- a/lib/compiler-llvm/src/translator/code.rs +++ b/lib/compiler-llvm/src/translator/code.rs @@ -2187,47 +2187,6 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> { ); let func_index = self.state.pop1()?.into_int_value(); - // We assume the table has the `anyfunc` element type. - let casted_table_base = self.builder.build_pointer_cast( - table_base, - self.intrinsics.anyfunc_ty.ptr_type(AddressSpace::Generic), - "casted_table_base", - ); - - let anyfunc_struct_ptr = unsafe { - self.builder.build_in_bounds_gep( - casted_table_base, - &[func_index], - "anyfunc_struct_ptr", - ) - }; - - // Load things from the anyfunc data structure. - let (func_ptr, found_dynamic_sigindex, ctx_ptr) = ( - self.builder - .build_load( - self.builder - .build_struct_gep(anyfunc_struct_ptr, 0, "func_ptr_ptr") - .unwrap(), - "func_ptr", - ) - .into_pointer_value(), - self.builder - .build_load( - self.builder - .build_struct_gep(anyfunc_struct_ptr, 1, "sigindex_ptr") - .unwrap(), - "sigindex", - ) - .into_int_value(), - self.builder.build_load( - self.builder - .build_struct_gep(anyfunc_struct_ptr, 2, "ctx_ptr_ptr") - .unwrap(), - "ctx_ptr", - ), - ); - let truncated_table_bounds = self.builder.build_int_truncate( table_bound, self.intrinsics.i32_ty, @@ -2280,8 +2239,85 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> { self.builder.build_unreachable(); self.builder.position_at_end(in_bounds_continue_block); + // We assume the table has the `funcref` (pointer to `anyfunc`) + // element type. + let casted_table_base = self.builder.build_pointer_cast( + table_base, + self.intrinsics.funcref_ty.ptr_type(AddressSpace::Generic), + "casted_table_base", + ); + + let funcref_ptr = unsafe { + self.builder.build_in_bounds_gep( + casted_table_base, + &[func_index], + "funcref_ptr", + ) + }; + + // a funcref (pointer to `anyfunc`) + let anyfunc_struct_ptr = self + .builder + .build_load(funcref_ptr, "anyfunc_struct_ptr") + .into_pointer_value(); + + // trap if we're trying to call a null funcref + { + let funcref_not_null = self + .builder + .build_is_not_null(anyfunc_struct_ptr, "null funcref check"); + + let funcref_continue_deref_block = self + .context + .append_basic_block(self.function, "funcref_continue deref_block"); + + let funcref_is_null_block = self + .context + .append_basic_block(self.function, "funcref_is_null_block"); + self.builder.build_conditional_branch( + funcref_not_null, + funcref_continue_deref_block, + funcref_is_null_block, + ); + self.builder.position_at_end(funcref_is_null_block); + self.builder.build_call( + self.intrinsics.throw_trap, + &[self.intrinsics.trap_call_indirect_null], + "throw", + ); + self.builder.build_unreachable(); + self.builder.position_at_end(funcref_continue_deref_block); + } + + // Load things from the anyfunc data structure. + let (func_ptr, found_dynamic_sigindex, ctx_ptr) = ( + self.builder + .build_load( + self.builder + .build_struct_gep(anyfunc_struct_ptr, 0, "func_ptr_ptr") + .unwrap(), + "func_ptr", + ) + .into_pointer_value(), + self.builder + .build_load( + self.builder + .build_struct_gep(anyfunc_struct_ptr, 1, "sigindex_ptr") + .unwrap(), + "sigindex", + ) + .into_int_value(), + self.builder.build_load( + self.builder + .build_struct_gep(anyfunc_struct_ptr, 2, "ctx_ptr_ptr") + .unwrap(), + "ctx_ptr", + ), + ); + // Next, check if the table element is initialized. + // TODO: we may not need this check anymore let elem_initialized = self.builder.build_is_not_null(func_ptr, ""); // Next, check if the signature id is correct. @@ -3118,8 +3154,8 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> { let v2 = self.builder.build_and(v2, mask, ""); let lhs = self.builder.build_right_shift(v1, v2, false, ""); let rhs = { - let int_width = self.intrinsics.i64_ty.const_int(64 as u64, false); - let rhs = self.builder.build_int_sub(int_width, v2, ""); + let negv2 = self.builder.build_int_neg(v2, ""); + let rhs = self.builder.build_and(negv2, mask, ""); self.builder.build_left_shift(v1, rhs, "") }; let res = self.builder.build_or(lhs, rhs, ""); @@ -9350,6 +9386,215 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> { size.add_attribute(AttributeLoc::Function, self.intrinsics.readonly); self.state.push1(size.try_as_basic_value().left().unwrap()); } + /*************************** + * Reference types. + * https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md + ***************************/ + Operator::RefNull { ty } => { + let ty = wptype_to_type(ty).map_err(to_compile_error)?; + let ty = type_to_llvm(self.intrinsics, ty)?; + self.state.push1(ty.const_zero()); + } + Operator::RefIsNull => { + let value = self.state.pop1()?.into_pointer_value(); + let is_null = self.builder.build_is_null(value, ""); + let is_null = self + .builder + .build_int_z_extend(is_null, self.intrinsics.i32_ty, ""); + self.state.push1(is_null); + } + Operator::RefFunc { function_index } => { + let index = self + .intrinsics + .i32_ty + .const_int(function_index.into(), false) + .as_basic_value_enum(); + let value = self + .builder + .build_call(self.intrinsics.func_ref, &[self.ctx.basic(), index], "") + .try_as_basic_value() + .left() + .unwrap(); + self.state.push1(value); + } + Operator::TableGet { table } => { + let table_index = self + .intrinsics + .i32_ty + .const_int(table.into(), false) + .as_basic_value_enum(); + let elem = self.state.pop1()?; + let table_get = if let Some(_) = self + .wasm_module + .local_table_index(TableIndex::from_u32(table)) + { + self.intrinsics.table_get + } else { + self.intrinsics.imported_table_get + }; + let value = self + .builder + .build_call(table_get, &[self.ctx.basic(), table_index, elem], "") + .try_as_basic_value() + .left() + .unwrap(); + let value = self.builder.build_bitcast( + value, + type_to_llvm( + self.intrinsics, + self.wasm_module + .tables + .get(TableIndex::from_u32(table)) + .unwrap() + .ty, + )?, + "", + ); + self.state.push1(value); + } + Operator::TableSet { table } => { + let table_index = self + .intrinsics + .i32_ty + .const_int(table.into(), false) + .as_basic_value_enum(); + let (elem, value) = self.state.pop2()?; + let value = self + .builder + .build_bitcast(value, self.intrinsics.anyref_ty, ""); + let table_set = if let Some(_) = self + .wasm_module + .local_table_index(TableIndex::from_u32(table)) + { + self.intrinsics.table_set + } else { + self.intrinsics.imported_table_set + }; + self.builder.build_call( + table_set, + &[self.ctx.basic(), table_index, elem, value], + "", + ); + } + Operator::TableCopy { + dst_table, + src_table, + } => { + let (dst, src, len) = self.state.pop3()?; + let dst_table = self + .intrinsics + .i32_ty + .const_int(dst_table as u64, false) + .as_basic_value_enum(); + let src_table = self + .intrinsics + .i32_ty + .const_int(src_table as u64, false) + .as_basic_value_enum(); + self.builder.build_call( + self.intrinsics.table_copy, + &[self.ctx.basic(), dst_table, src_table, dst, src, len], + "", + ); + } + Operator::TableInit { segment, table } => { + let (dst, src, len) = self.state.pop3()?; + let segment = self + .intrinsics + .i32_ty + .const_int(segment as u64, false) + .as_basic_value_enum(); + let table = self + .intrinsics + .i32_ty + .const_int(table as u64, false) + .as_basic_value_enum(); + self.builder.build_call( + self.intrinsics.table_init, + &[self.ctx.basic(), table, segment, dst, src, len], + "", + ); + } + Operator::ElemDrop { segment } => { + let segment = self + .intrinsics + .i32_ty + .const_int(segment as u64, false) + .as_basic_value_enum(); + self.builder.build_call( + self.intrinsics.elem_drop, + &[self.ctx.basic(), segment], + "", + ); + } + Operator::TableFill { table } => { + let table = self + .intrinsics + .i32_ty + .const_int(table as u64, false) + .as_basic_value_enum(); + let (start, elem, len) = self.state.pop3()?; + let elem = self + .builder + .build_bitcast(elem, self.intrinsics.anyref_ty, ""); + self.builder.build_call( + self.intrinsics.table_fill, + &[self.ctx.basic(), table, start, elem, len], + "", + ); + } + Operator::TableGrow { table } => { + let (elem, delta) = self.state.pop2()?; + let elem = self + .builder + .build_bitcast(elem, self.intrinsics.anyref_ty, ""); + let (table_grow, table_index) = if let Some(local_table_index) = self + .wasm_module + .local_table_index(TableIndex::from_u32(table)) + { + (self.intrinsics.table_grow, local_table_index.as_u32()) + } else { + (self.intrinsics.imported_table_grow, table) + }; + let table_index = self + .intrinsics + .i32_ty + .const_int(table_index as u64, false) + .as_basic_value_enum(); + let size = self + .builder + .build_call( + table_grow, + &[self.ctx.basic(), elem, delta, table_index], + "", + ) + .try_as_basic_value() + .left() + .unwrap(); + self.state.push1(size); + } + Operator::TableSize { table } => { + let (table_size, table_index) = if let Some(local_table_index) = self + .wasm_module + .local_table_index(TableIndex::from_u32(table)) + { + (self.intrinsics.table_size, local_table_index.as_u32()) + } else { + (self.intrinsics.imported_table_size, table) + }; + let table_index = self + .intrinsics + .i32_ty + .const_int(table_index as u64, false) + .as_basic_value_enum(); + let size = self + .builder + .build_call(table_size, &[self.ctx.basic(), table_index], "") + .try_as_basic_value() + .left() + .unwrap(); + self.state.push1(size); + } _ => { return Err(CompileError::Codegen(format!( "Operator {:?} unimplemented", diff --git a/lib/compiler-llvm/src/translator/intrinsics.rs b/lib/compiler-llvm/src/translator/intrinsics.rs index a0df2e246..a2274cac5 100644 --- a/lib/compiler-llvm/src/translator/intrinsics.rs +++ b/lib/compiler-llvm/src/translator/intrinsics.rs @@ -39,10 +39,8 @@ pub fn type_to_llvm_ptr<'ctx>( Type::F32 => Ok(intrinsics.f32_ptr_ty), Type::F64 => Ok(intrinsics.f64_ptr_ty), Type::V128 => Ok(intrinsics.i128_ptr_ty), - ty => Err(CompileError::Codegen(format!( - "type_to_llvm: unimplemented wasmer_types type {:?}", - ty - ))), + Type::FuncRef => Ok(intrinsics.funcref_ty.ptr_type(AddressSpace::Generic)), + Type::ExternRef => Ok(intrinsics.externref_ty.ptr_type(AddressSpace::Generic)), } } @@ -56,10 +54,8 @@ pub fn type_to_llvm<'ctx>( Type::F32 => Ok(intrinsics.f32_ty.as_basic_type_enum()), Type::F64 => Ok(intrinsics.f64_ty.as_basic_type_enum()), Type::V128 => Ok(intrinsics.i128_ty.as_basic_type_enum()), - ty => Err(CompileError::Codegen(format!( - "type_to_llvm: unimplemented wasmer_types type {:?}", - ty - ))), + Type::FuncRef => Ok(intrinsics.funcref_ty.as_basic_type_enum()), + Type::ExternRef => Ok(intrinsics.externref_ty.as_basic_type_enum()), } } @@ -147,6 +143,10 @@ pub struct Intrinsics<'ctx> { pub anyfunc_ty: StructType<'ctx>, + pub funcref_ty: PointerType<'ctx>, + pub externref_ty: PointerType<'ctx>, + pub anyref_ty: PointerType<'ctx>, + pub i1_zero: IntValue<'ctx>, pub i8_zero: IntValue<'ctx>, pub i32_zero: IntValue<'ctx>, @@ -167,11 +167,25 @@ pub struct Intrinsics<'ctx> { pub trap_unaligned_atomic: BasicValueEnum<'ctx>, pub trap_table_access_oob: BasicValueEnum<'ctx>, - // VM intrinsics. - pub throw_trap: FunctionValue<'ctx>, - pub experimental_stackmap: FunctionValue<'ctx>, + // VM libcalls. + pub table_copy: FunctionValue<'ctx>, + pub table_init: FunctionValue<'ctx>, + pub table_fill: FunctionValue<'ctx>, + pub table_size: FunctionValue<'ctx>, + pub imported_table_size: FunctionValue<'ctx>, + pub table_get: FunctionValue<'ctx>, + pub imported_table_get: FunctionValue<'ctx>, + pub table_set: FunctionValue<'ctx>, + pub imported_table_set: FunctionValue<'ctx>, + pub table_grow: FunctionValue<'ctx>, + pub imported_table_grow: FunctionValue<'ctx>, + pub func_ref: FunctionValue<'ctx>, + pub elem_drop: FunctionValue<'ctx>, + pub throw_trap: FunctionValue<'ctx>, + + // VM builtins. pub vmfunction_import_ptr_ty: PointerType<'ctx>, pub vmfunction_import_body_element: u32, pub vmfunction_import_vmctx_element: u32, @@ -185,6 +199,7 @@ pub struct Intrinsics<'ctx> { pub memory32_size_ptr_ty: PointerType<'ctx>, pub imported_memory32_size_ptr_ty: PointerType<'ctx>, + // Pointer to the VM. pub ctx_ptr_ty: PointerType<'ctx>, } @@ -251,6 +266,9 @@ impl<'ctx> Intrinsics<'ctx> { ], false, ); + let funcref_ty = anyfunc_ty.ptr_type(AddressSpace::Generic); + let externref_ty = funcref_ty; + let anyref_ty = i8_ptr_ty; let ret_i8x16_take_i8x16_i8x16 = i8x16_ty.fn_type(&[i8x16_ty_basic, i8x16_ty_basic], false); let ret_i16x8_take_i16x8_i16x8 = i16x8_ty.fn_type(&[i16x8_ty_basic, i16x8_ty_basic], false); @@ -369,7 +387,7 @@ impl<'ctx> Intrinsics<'ctx> { ), readonly: context .create_enum_attribute(Attribute::get_named_enum_kind_id("readonly"), 0), - stack_probe: context.create_string_attribute("probe-stack", "wasmer_probestack"), + stack_probe: context.create_string_attribute("probe-stack", "wasmer_vm_probestack"), void_ty, i1_ty, @@ -399,6 +417,10 @@ impl<'ctx> Intrinsics<'ctx> { anyfunc_ty, + funcref_ty, + externref_ty, + anyref_ty, + i1_zero, i8_zero, i32_zero, @@ -437,12 +459,6 @@ impl<'ctx> Intrinsics<'ctx> { .const_int(TrapCode::TableAccessOutOfBounds as _, false) .as_basic_value_enum(), - // VM intrinsics. - throw_trap: module.add_function( - "wasmer_raise_trap", - void_ty.fn_type(&[i32_ty_basic], false), - None, - ), experimental_stackmap: module.add_function( "llvm.experimental.stackmap", void_ty.fn_type( @@ -455,6 +471,145 @@ impl<'ctx> Intrinsics<'ctx> { None, ), + // VM libcalls. + table_copy: module.add_function( + "wasmer_vm_table_copy", + void_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + ], + false, + ), + None, + ), + table_init: module.add_function( + "wasmer_vm_table_init", + void_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + ], + false, + ), + None, + ), + table_fill: module.add_function( + "wasmer_vm_table_fill", + void_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + anyref_ty.as_basic_type_enum(), + i32_ty_basic, + ], + false, + ), + None, + ), + table_size: module.add_function( + "wasmer_vm_table_size", + i32_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false), + None, + ), + imported_table_size: module.add_function( + "wasmer_vm_imported_table_size", + i32_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false), + None, + ), + table_get: module.add_function( + "wasmer_vm_table_get", + anyref_ty.fn_type( + &[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic, i32_ty_basic], + false, + ), + None, + ), + imported_table_get: module.add_function( + "wasmer_vm_imported_table_get", + anyref_ty.fn_type( + &[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic, i32_ty_basic], + false, + ), + None, + ), + table_set: module.add_function( + "wasmer_vm_table_set", + void_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + anyref_ty.as_basic_type_enum(), + ], + false, + ), + None, + ), + imported_table_set: module.add_function( + "wasmer_vm_imported_table_set", + void_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + anyref_ty.as_basic_type_enum(), + ], + false, + ), + None, + ), + table_grow: module.add_function( + "wasmer_vm_table_grow", + i32_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + anyref_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + ], + false, + ), + None, + ), + imported_table_grow: module.add_function( + "wasmer_vm_imported_table_grow", + i32_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + anyref_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + ], + false, + ), + None, + ), + func_ref: module.add_function( + "wasmer_vm_func_ref", + funcref_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false), + None, + ), + elem_drop: module.add_function( + "wasmer_vm_elem_drop", + void_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false), + None, + ), + throw_trap: module.add_function( + "wasmer_vm_raise_trap", + void_ty.fn_type(&[i32_ty_basic], false), + None, + ), + vmfunction_import_ptr_ty: context .struct_type(&[i8_ptr_ty_basic, i8_ptr_ty_basic], false) .ptr_type(AddressSpace::Generic), @@ -490,13 +645,14 @@ impl<'ctx> Intrinsics<'ctx> { ctx_ptr_ty, }; - // TODO: mark vmctx args as nofree, align 16, dereferenceable(?) - let noreturn = context.create_enum_attribute(Attribute::get_named_enum_kind_id("noreturn"), 0); intrinsics .throw_trap .add_attribute(AttributeLoc::Function, noreturn); + intrinsics + .func_ref + .add_attribute(AttributeLoc::Function, intrinsics.readonly); intrinsics } diff --git a/lib/compiler-llvm/src/translator/state.rs b/lib/compiler-llvm/src/translator/state.rs index b1562183e..e3046a09d 100644 --- a/lib/compiler-llvm/src/translator/state.rs +++ b/lib/compiler-llvm/src/translator/state.rs @@ -315,6 +315,22 @@ impl<'ctx> State<'ctx> { Ok((v1, v2)) } + pub fn pop3( + &mut self, + ) -> Result< + ( + BasicValueEnum<'ctx>, + BasicValueEnum<'ctx>, + BasicValueEnum<'ctx>, + ), + CompileError, + > { + let v3 = self.pop1()?; + let v2 = self.pop1()?; + let v1 = self.pop1()?; + Ok((v1, v2, v3)) + } + pub fn pop3_extra( &mut self, ) -> Result< diff --git a/lib/compiler-singlepass/src/codegen_x64.rs b/lib/compiler-singlepass/src/codegen_x64.rs index eef73dd5e..4b5c30b0d 100644 --- a/lib/compiler-singlepass/src/codegen_x64.rs +++ b/lib/compiler-singlepass/src/codegen_x64.rs @@ -186,20 +186,14 @@ trait PopMany { impl PopMany for Vec { fn peek1(&self) -> Result<&T, CodegenError> { - match self.last() { - Some(x) => Ok(x), - None => Err(CodegenError { - message: "peek1() expects at least 1 element".into(), - }), - } + self.last().ok_or_else(|| CodegenError { + message: "peek1() expects at least 1 element".into(), + }) } fn pop1(&mut self) -> Result { - match self.pop() { - Some(x) => Ok(x), - None => Err(CodegenError { - message: "pop1() expects at least 1 element".into(), - }), - } + self.pop().ok_or_else(|| CodegenError { + message: "pop1() expects at least 1 element".into(), + }) } fn pop2(&mut self) -> Result<(T, T), CodegenError> { if self.len() < 2 { @@ -5247,11 +5241,8 @@ impl<'a> FuncGen<'a> { } } Operator::CallIndirect { index, table_index } => { - if table_index != 0 { - return Err(CodegenError { - message: "CallIndirect: table_index is not 0".to_string(), - }); - } + // TODO: removed restriction on always being table idx 0; + // does any code depend on this? let table_index = TableIndex::new(table_index as _); let index = SignatureIndex::new(index as usize); let sig = self.module.signatures.get(index).unwrap(); @@ -5341,15 +5332,25 @@ impl<'a> FuncGen<'a> { .emit_jmp(Condition::BelowEqual, self.special_labels.table_access_oob); self.assembler .emit_mov(Size::S32, func_index, Location::GPR(table_count)); - self.assembler.emit_imul_imm32_gpr64( - self.vmoffsets.size_of_vmcaller_checked_anyfunc() as u32, - table_count, - ); + self.assembler + .emit_imul_imm32_gpr64(self.vmoffsets.size_of_vm_funcref() as u32, table_count); self.assembler.emit_add( Size::S64, Location::GPR(table_base), Location::GPR(table_count), ); + + // deref the table to get a VMFuncRef + self.assembler.emit_mov( + Size::S64, + Location::Memory(table_count, self.vmoffsets.vm_funcref_anyfunc_ptr() as i32), + Location::GPR(table_count), + ); + // Trap if the FuncRef is null + self.assembler + .emit_cmp(Size::S64, Location::Imm32(0), Location::GPR(table_count)); + self.assembler + .emit_jmp(Condition::Equal, self.special_labels.indirect_call_null); self.assembler.emit_mov( Size::S64, Location::Memory( @@ -5359,18 +5360,6 @@ impl<'a> FuncGen<'a> { Location::GPR(sigidx), ); - // Trap if the current table entry is null. - self.assembler.emit_cmp( - Size::S64, - Location::Imm32(0), - Location::Memory( - table_count, - (self.vmoffsets.vmcaller_checked_anyfunc_func_ptr() as usize) as i32, - ), - ); - self.assembler - .emit_jmp(Condition::Equal, self.special_labels.indirect_call_null); - // Trap if signature mismatches. self.assembler.emit_cmp( Size::S32, @@ -8161,6 +8150,351 @@ impl<'a> FuncGen<'a> { self.assembler.emit_pop(Size::S64, Location::GPR(value)); self.machine.release_temp_gpr(compare); } + + Operator::RefNull { .. } => { + self.value_stack.push(Location::Imm64(0)); + self.machine + .state + .wasm_stack + .push(WasmAbstractValue::Const(0)); + } + Operator::RefFunc { function_index } => { + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_func_ref_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: unclear if we need this? check other new insts with no stack ops + // self.machine.release_locations_only_osr_state(1); + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, func_index] -> funcref + iter::once(Location::Imm32(function_index as u32)), + )?; + + let ret = self.machine.acquire_locations( + &mut self.assembler, + &[( + WpType::FuncRef, + MachineValue::WasmStack(self.value_stack.len()), + )], + false, + )[0]; + self.value_stack.push(ret); + self.assembler + .emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::RefIsNull => { + self.emit_cmpop_i64_dynamic_b(Condition::Equal, Location::Imm64(0))?; + } + Operator::TableSet { table: index } => { + let table_index = TableIndex::new(index as _); + let value = self.value_stack.pop().unwrap(); + let index = self.value_stack.pop().unwrap(); + // double check this does what I think it does + self.machine.release_locations_only_regs(&[value, index]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function( + if self.module.local_table_index(table_index).is_some() { + VMBuiltinFunctionIndex::get_table_set_index() + } else { + VMBuiltinFunctionIndex::get_imported_table_set_index() + }, + ) as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 2? + self.machine.release_locations_only_osr_state(1); + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, table_index, elem_index, reftype] + [Location::Imm32(table_index.index() as u32), index, value] + .iter() + .cloned(), + )?; + + self.machine + .release_locations_only_stack(&mut self.assembler, &[index, value]); + } + Operator::TableGet { table: index } => { + let table_index = TableIndex::new(index as _); + let index = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[index]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function( + if self.module.local_table_index(table_index).is_some() { + VMBuiltinFunctionIndex::get_table_get_index() + } else { + VMBuiltinFunctionIndex::get_imported_table_get_index() + }, + ) as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.machine.release_locations_only_osr_state(1); + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, table_index, elem_index] -> reftype + [Location::Imm32(table_index.index() as u32), index] + .iter() + .cloned(), + )?; + + self.machine + .release_locations_only_stack(&mut self.assembler, &[index]); + + let ret = self.machine.acquire_locations( + &mut self.assembler, + &[( + WpType::FuncRef, + MachineValue::WasmStack(self.value_stack.len()), + )], + false, + )[0]; + self.value_stack.push(ret); + self.assembler + .emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::TableSize { table: index } => { + let table_index = TableIndex::new(index as _); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function( + if self.module.local_table_index(table_index).is_some() { + VMBuiltinFunctionIndex::get_table_size_index() + } else { + VMBuiltinFunctionIndex::get_imported_table_size_index() + }, + ) as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, table_index] -> i32 + iter::once(Location::Imm32(table_index.index() as u32)), + )?; + + let ret = self.machine.acquire_locations( + &mut self.assembler, + &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + self.assembler + .emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); + } + Operator::TableGrow { table: index } => { + let table_index = TableIndex::new(index as _); + let delta = self.value_stack.pop().unwrap(); + let init_value = self.value_stack.pop().unwrap(); + self.machine + .release_locations_only_regs(&[delta, init_value]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function( + if self.module.local_table_index(table_index).is_some() { + VMBuiltinFunctionIndex::get_table_grow_index() + } else { + VMBuiltinFunctionIndex::get_imported_table_get_index() + }, + ) as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 2? + self.machine.release_locations_only_osr_state(1); + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, init_value, delta, table_index] -> u32 + [ + init_value, + delta, + Location::Imm32(table_index.index() as u32), + ] + .iter() + .cloned(), + )?; + + self.machine + .release_locations_only_stack(&mut self.assembler, &[init_value, delta]); + + let ret = self.machine.acquire_locations( + &mut self.assembler, + &[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))], + false, + )[0]; + self.value_stack.push(ret); + self.assembler + .emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); + } + Operator::TableCopy { + dst_table, + src_table, + } => { + let len = self.value_stack.pop().unwrap(); + let src = self.value_stack.pop().unwrap(); + let dest = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[len, src, dest]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_copy_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 3? + self.machine.release_locations_only_osr_state(1); + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, dst_table_index, src_table_index, dst, src, len] + [ + Location::Imm32(dst_table), + Location::Imm32(src_table), + dest, + src, + len, + ] + .iter() + .cloned(), + )?; + + self.machine + .release_locations_only_stack(&mut self.assembler, &[dest, src, len]); + } + + Operator::TableFill { table } => { + let len = self.value_stack.pop().unwrap(); + let val = self.value_stack.pop().unwrap(); + let dest = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[len, val, dest]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_fill_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 3? + self.machine.release_locations_only_osr_state(1); + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, table_index, start_idx, item, len] + [Location::Imm32(table), dest, val, len].iter().cloned(), + )?; + + self.machine + .release_locations_only_stack(&mut self.assembler, &[dest, val, len]); + } + Operator::TableInit { segment, table } => { + let len = self.value_stack.pop().unwrap(); + let src = self.value_stack.pop().unwrap(); + let dest = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[len, src, dest]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_init_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 3? + self.machine.release_locations_only_osr_state(1); + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, table_index, elem_index, dst, src, len] + [ + Location::Imm32(table), + Location::Imm32(segment), + dest, + src, + len, + ] + .iter() + .cloned(), + )?; + + self.machine + .release_locations_only_stack(&mut self.assembler, &[dest, src, len]); + } + Operator::ElemDrop { segment } => { + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_elem_drop_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: do we need this? + //self.machine.release_locations_only_osr_state(1); + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, elem_index] + [Location::Imm32(segment)].iter().cloned(), + )?; + } _ => { return Err(CodegenError { message: format!("not yet implemented: {:?}", op), diff --git a/lib/compiler-singlepass/src/machine.rs b/lib/compiler-singlepass/src/machine.rs index 7f446744c..3cf01512f 100644 --- a/lib/compiler-singlepass/src/machine.rs +++ b/lib/compiler-singlepass/src/machine.rs @@ -156,7 +156,8 @@ impl Machine { let loc = match *ty { WpType::F32 | WpType::F64 => self.pick_xmm().map(Location::XMM), WpType::I32 | WpType::I64 => self.pick_gpr().map(Location::GPR), - _ => unreachable!(), + WpType::FuncRef | WpType::ExternRef => self.pick_gpr().map(Location::GPR), + _ => unreachable!("can't acquire location for type {:?}", ty), }; let loc = if let Some(x) = loc { diff --git a/lib/compiler/src/translator/environ.rs b/lib/compiler/src/translator/environ.rs index 156a3d65a..2996c6d67 100644 --- a/lib/compiler/src/translator/environ.rs +++ b/lib/compiler/src/translator/environ.rs @@ -331,14 +331,6 @@ impl<'data> ModuleEnvironment<'data> { Ok(()) } - pub(crate) fn reserve_passive_elements(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .passive_elements - .reserve_exact(usize::try_from(num).unwrap()); - Ok(()) - } - pub(crate) fn declare_table_initializers( &mut self, table_index: TableIndex, @@ -363,7 +355,16 @@ impl<'data> ModuleEnvironment<'data> { elem_index: ElemIndex, segments: Box<[FunctionIndex]>, ) -> WasmResult<()> { - self.result.module.passive_elements[elem_index] = segments; + let old = self + .result + .module + .passive_elements + .insert(elem_index, segments); + debug_assert!( + old.is_none(), + "should never get duplicate element indices, that would be a bug in `wasmer_compiler`'s \ + translation" + ); Ok(()) } @@ -406,7 +407,8 @@ impl<'data> ModuleEnvironment<'data> { } pub(crate) fn reserve_passive_data(&mut self, count: u32) -> WasmResult<()> { - self.result.module.passive_data.reserve(count as usize); + let count = usize::try_from(count).unwrap(); + self.result.module.passive_data.reserve(count); Ok(()) } @@ -415,7 +417,15 @@ impl<'data> ModuleEnvironment<'data> { data_index: DataIndex, data: &'data [u8], ) -> WasmResult<()> { - self.result.module.passive_data[data_index] = Arc::from(data); + let old = self + .result + .module + .passive_data + .insert(data_index, Arc::from(data)); + debug_assert!( + old.is_none(), + "a module can't have duplicate indices, this would be a wasmer-compiler bug" + ); Ok(()) } diff --git a/lib/compiler/src/translator/sections.rs b/lib/compiler/src/translator/sections.rs index f3373b7f7..0cd234a57 100644 --- a/lib/compiler/src/translator/sections.rs +++ b/lib/compiler/src/translator/sections.rs @@ -332,7 +332,6 @@ pub fn parse_element_section<'data>( environ: &mut ModuleEnvironment, ) -> WasmResult<()> { environ.reserve_table_initializers(elements.get_count())?; - environ.reserve_passive_elements(elements.get_count())?; for (index, entry) in elements.into_iter().enumerate() { let Element { kind, items, ty } = entry?; @@ -372,7 +371,7 @@ pub fn parse_element_section<'data>( let index = ElemIndex::from_u32(index as u32); environ.declare_passive_element(index, segments)?; } - ElementKind::Declared => return Err(wasm_unsupported!("element kind declared")), + ElementKind::Declared => (), } } Ok(()) diff --git a/lib/deprecated/runtime-core/Cargo.lock b/lib/deprecated/runtime-core/Cargo.lock index ea1fad1c4..5cd56d0c4 100644 --- a/lib/deprecated/runtime-core/Cargo.lock +++ b/lib/deprecated/runtime-core/Cargo.lock @@ -6,7 +6,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" dependencies = [ - "gimli", + "gimli 0.22.0", ] [[package]] @@ -134,25 +134,25 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "cranelift-bforest" -version = "0.68.0" +version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9221545c0507dc08a62b2d8b5ffe8e17ac580b0a74d1813b496b8d70b070fbd0" +checksum = "31f782ffb172d8095cbb4c6464d85432c3bcfa8609b0bb1dc27cfd35bd90e052" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.68.0" +version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9936ea608b6cd176f107037f6adbb4deac933466fc7231154f96598b2d3ab1" +checksum = "91e0910022b490bd0a65d5baa1693b0475cdbeea1c26472343f2acea1f1f55b8" dependencies = [ "byteorder", "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", - "gimli", + "gimli 0.23.0", "log", "regalloc", "smallvec", @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.68.0" +version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ef2b2768568306540f4c8db3acce9105534d34c4a1e440529c1e702d7f8c8d7" +checksum = "7cafe95cb5ac659e113549b2794a2c8d3a14f36e1a98728a6e0ea7a773be2129" dependencies = [ "cranelift-codegen-shared", "cranelift-entity", @@ -172,24 +172,24 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.68.0" +version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6759012d6d19c4caec95793f052613e9d4113e925e7f14154defbac0f1d4c938" +checksum = "8d1bd002e42cc094a131a8227d06d48df28ea3b9127e5e3bc3010e079858e9af" [[package]] name = "cranelift-entity" -version = "0.68.0" +version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86badbce14e15f52a45b666b38abe47b204969dd7f8fb7488cb55dd46b361fa6" +checksum = "e55e9043403f0dec775f317280015150e78b2352fb947d2f37407fd4ce6311c7" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.68.0" +version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b608bb7656c554d0a4cf8f50c7a10b857e80306f6ff829ad6d468a7e2323c8d8" +checksum = "0153680ebce89aac7cad90a5442bb136faacfc86ea62587a01b8e8e79f8249c9" dependencies = [ "cranelift-codegen", "log", @@ -398,6 +398,12 @@ name = "gimli" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" + +[[package]] +name = "gimli" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" dependencies = [ "fallible-iterator", "indexmap", @@ -406,9 +412,9 @@ dependencies = [ [[package]] name = "goblin" -version = "0.2.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20fd25aa456527ce4f544271ae4fea65d2eda4a6561ea56f39fb3ee4f7e3884" +checksum = "669cdc3826f69a51d3f8fc3f86de81c2378110254f678b8407977736122057a4" dependencies = [ "log", "plain", @@ -458,9 +464,9 @@ dependencies = [ [[package]] name = "inkwell" -version = "0.1.0-llvm10sample" +version = "0.1.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e079c12273d96e41481454a37ad968e607e1ce51b39b9facd3a802a12df6e9dc" +checksum = "f5fe0be1e47c0c0f3da4397693e08f5d78329ae095c25d529e12ade78420fb41" dependencies = [ "either", "inkwell_internals", @@ -473,9 +479,9 @@ dependencies = [ [[package]] name = "inkwell_internals" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b22cf4eda09069b48204cce4b7cd9a25311da813780e95a038524f2210fab44e" +checksum = "c2e1f71330ccec54ee62533ae88574c4169b67fb4b95cbb1196a1322582abd11" dependencies = [ "proc-macro2", "quote", @@ -493,9 +499,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" dependencies = [ "either", ] @@ -520,24 +526,25 @@ checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9" [[package]] name = "libloading" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cadb8e769f070c45df05c78c7520eb4cd17061d4ab262e43cfc68b4d00ac71c" +checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" dependencies = [ + "cfg-if 1.0.0", "winapi", ] [[package]] name = "llvm-sys" -version = "100.1.0" +version = "110.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5545bf9a09267c644e4d0ac68f37ac200af6579ae2e82aebce382654eb4abab1" +checksum = "21ede189444b8c78907e5d36da5dabcf153170fcff9c1dba48afc4b33c7e19f0" dependencies = [ "cc", "lazy_static", "libc", "regex", - "semver 0.9.0", + "semver 0.11.0", ] [[package]] @@ -643,9 +650,9 @@ checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" [[package]] name = "object" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" dependencies = [ "crc32fast", "indexmap", @@ -653,9 +660,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.4.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "parking_lot" @@ -789,17 +796,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "raw-cpuid" -version = "7.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a349ca83373cfa5d6dbb66fd76e58b2cca08da71a5f6400de0a0a6a9bceeaf" -dependencies = [ - "bitflags", - "cc", - "rustc_version", -] - [[package]] name = "rayon" version = "1.5.0" @@ -1148,7 +1144,7 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasmer" -version = "1.0.1" +version = "1.0.2" dependencies = [ "cfg-if 0.1.10", "indexmap", @@ -1171,7 +1167,7 @@ dependencies = [ [[package]] name = "wasmer-cache" -version = "1.0.1" +version = "1.0.2" dependencies = [ "blake3", "hex", @@ -1181,10 +1177,9 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "1.0.1" +version = "1.0.2" dependencies = [ "enumset", - "raw-cpuid", "serde", "serde_bytes", "smallvec", @@ -1197,11 +1192,11 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "1.0.1" +version = "1.0.2" dependencies = [ "cranelift-codegen", "cranelift-frontend", - "gimli", + "gimli 0.23.0", "more-asserts", "rayon", "serde", @@ -1214,7 +1209,7 @@ dependencies = [ [[package]] name = "wasmer-compiler-llvm" -version = "1.0.1" +version = "1.0.2" dependencies = [ "byteorder", "cc", @@ -1236,7 +1231,7 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "1.0.1" +version = "1.0.2" dependencies = [ "byteorder", "dynasm", @@ -1253,7 +1248,7 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "1.0.1" +version = "1.0.2" dependencies = [ "proc-macro-error", "proc-macro2", @@ -1263,7 +1258,7 @@ dependencies = [ [[package]] name = "wasmer-engine" -version = "1.0.1" +version = "1.0.2" dependencies = [ "backtrace", "bincode", @@ -1282,7 +1277,7 @@ dependencies = [ [[package]] name = "wasmer-engine-jit" -version = "1.0.1" +version = "1.0.2" dependencies = [ "bincode", "cfg-if 0.1.10", @@ -1298,7 +1293,7 @@ dependencies = [ [[package]] name = "wasmer-engine-native" -version = "1.0.1" +version = "1.0.2" dependencies = [ "bincode", "cfg-if 0.1.10", @@ -1317,9 +1312,9 @@ dependencies = [ [[package]] name = "wasmer-object" -version = "1.0.1" +version = "1.0.2" dependencies = [ - "object 0.22.0", + "object 0.23.0", "thiserror", "wasmer-compiler", "wasmer-types", @@ -1345,7 +1340,7 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "1.0.1" +version = "1.0.2" dependencies = [ "cranelift-entity", "serde", @@ -1354,7 +1349,7 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "1.0.1" +version = "1.0.2" dependencies = [ "backtrace", "cc", @@ -1372,9 +1367,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.65.0" +version = "0.74.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc2fe6350834b4e528ba0901e7aa405d78b89dc1fa3145359eb4de0e323fcf" +checksum = "4a4d63608421d9a22d4bce220f2841f3b609a5aaabd3ed3aeeb5fed2702c3c78" [[package]] name = "wast" diff --git a/lib/deprecated/runtime-core/src/table.rs b/lib/deprecated/runtime-core/src/table.rs index 9b2c60d1d..c0e966d1f 100644 --- a/lib/deprecated/runtime-core/src/table.rs +++ b/lib/deprecated/runtime-core/src/table.rs @@ -100,7 +100,7 @@ mod table_tests { minimum: 10, maximum: Some(20), }, - Value::null(), + Value::FuncRef(None), ) .unwrap(); assert_eq!(table.size(), 10); diff --git a/lib/emscripten/src/env/unix/mod.rs b/lib/emscripten/src/env/unix/mod.rs index 16746e5e4..6355fb3ec 100644 --- a/lib/emscripten/src/env/unix/mod.rs +++ b/lib/emscripten/src/env/unix/mod.rs @@ -150,10 +150,11 @@ pub fn _gai_strerror(ctx: &EmEnv, ecode: i32) -> i32 { let cstr = unsafe { std::ffi::CStr::from_ptr(libc::gai_strerror(ecode)) }; let bytes = cstr.to_bytes_with_nul(); let string_on_guest: WasmPtr = call_malloc_with_cast(ctx, bytes.len() as _); + let memory = ctx.memory(0); let writer = unsafe { string_on_guest - .deref_mut(ctx.memory(0), 0, bytes.len() as _) + .deref_mut(&memory, 0, bytes.len() as _) .unwrap() }; for (i, byte) in bytes.iter().enumerate() { @@ -175,7 +176,7 @@ pub fn _getaddrinfo( let memory = ctx.memory(0); debug!(" => node = {}", unsafe { node_ptr - .deref(memory) + .deref(&memory) .map(|np| { std::ffi::CStr::from_ptr(np as *const Cell as *const c_char) .to_string_lossy() @@ -184,7 +185,7 @@ pub fn _getaddrinfo( }); debug!(" => server_str = {}", unsafe { service_str_ptr - .deref(memory) + .deref(&memory) .map(|np| { std::ffi::CStr::from_ptr(np as *const Cell as *const c_char) .to_string_lossy() @@ -192,7 +193,7 @@ pub fn _getaddrinfo( .unwrap_or(std::borrow::Cow::Borrowed("null")) }); - let hints = hints_ptr.deref(memory).map(|hints_memory| { + let hints = hints_ptr.deref(&memory).map(|hints_memory| { let hints_guest = hints_memory.get(); addrinfo { ai_flags: hints_guest.ai_flags, @@ -212,11 +213,11 @@ pub fn _getaddrinfo( let result = unsafe { libc::getaddrinfo( (node_ptr - .deref(memory) + .deref(&memory) .map(|m| m as *const Cell as *const c_char)) .unwrap_or(std::ptr::null()), service_str_ptr - .deref(memory) + .deref(&memory) .map(|m| m as *const Cell as *const c_char) .unwrap_or(std::ptr::null()), hints @@ -245,7 +246,7 @@ pub fn _getaddrinfo( // connect list if let Some(prev_guest) = previous_guest_node { - let mut pg = prev_guest.deref_mut(ctx.memory(0)).unwrap().get_mut(); + let mut pg = prev_guest.deref_mut(&memory).unwrap().get_mut(); pg.ai_next = current_guest_node_ptr; } @@ -257,10 +258,7 @@ pub fn _getaddrinfo( let host_sockaddr_ptr = (*current_host_node).ai_addr; let guest_sockaddr_ptr: WasmPtr = call_malloc_with_cast(ctx, host_addrlen as _); - let guest_sockaddr = guest_sockaddr_ptr - .deref_mut(ctx.memory(0)) - .unwrap() - .get_mut(); + let guest_sockaddr = guest_sockaddr_ptr.deref_mut(&memory).unwrap().get_mut(); guest_sockaddr.sa_family = (*host_sockaddr_ptr).sa_family as i16; guest_sockaddr.sa_data = (*host_sockaddr_ptr).sa_data; @@ -277,9 +275,8 @@ pub fn _getaddrinfo( let guest_canonname: WasmPtr = call_malloc_with_cast(ctx, str_size as _); - let guest_canonname_writer = guest_canonname - .deref(ctx.memory(0), 0, str_size as _) - .unwrap(); + let guest_canonname_writer = + guest_canonname.deref(&memory, 0, str_size as _).unwrap(); for (i, b) in canonname_bytes.into_iter().enumerate() { guest_canonname_writer[i].set(*b as _) } @@ -290,10 +287,8 @@ pub fn _getaddrinfo( } }; - let mut current_guest_node = current_guest_node_ptr - .deref_mut(ctx.memory(0)) - .unwrap() - .get_mut(); + let mut current_guest_node = + current_guest_node_ptr.deref_mut(&memory).unwrap().get_mut(); current_guest_node.ai_flags = (*current_host_node).ai_flags; current_guest_node.ai_family = (*current_host_node).ai_family; current_guest_node.ai_socktype = (*current_host_node).ai_socktype; @@ -311,7 +306,7 @@ pub fn _getaddrinfo( head_of_list.unwrap_or_else(|| WasmPtr::new(0)) }; - res_val_ptr.deref(ctx.memory(0)).unwrap().set(head_of_list); + res_val_ptr.deref(&memory).unwrap().set(head_of_list); 0 } diff --git a/lib/emscripten/src/env/windows/mod.rs b/lib/emscripten/src/env/windows/mod.rs index 874d5e5e6..9a2d00f7c 100644 --- a/lib/emscripten/src/env/windows/mod.rs +++ b/lib/emscripten/src/env/windows/mod.rs @@ -19,7 +19,8 @@ extern "C" { /// emscripten: _getenv // (name: *const char) -> *const c_char; pub fn _getenv(ctx: &EmEnv, name: u32) -> u32 { debug!("emscripten::_getenv"); - let name_string = read_string_from_wasm(ctx.memory(0), name); + let memory = ctx.memory(0); + let name_string = read_string_from_wasm(&memory, name); debug!("=> name({:?})", name_string); let c_str = unsafe { getenv(name_string.as_ptr() as *const libc::c_char) }; if c_str.is_null() { @@ -31,9 +32,10 @@ pub fn _getenv(ctx: &EmEnv, name: u32) -> u32 { /// emscripten: _setenv // (name: *const char, name: *const value, overwrite: int); pub fn _setenv(ctx: &EmEnv, name: u32, value: u32, _overwrite: u32) -> c_int { debug!("emscripten::_setenv"); + let memory = ctx.memory(0); // setenv does not exist on windows, so we hack it with _putenv - let name = read_string_from_wasm(ctx.memory(0), name); - let value = read_string_from_wasm(ctx.memory(0), value); + let name = read_string_from_wasm(&memory, name); + let value = read_string_from_wasm(&memory, value); let putenv_string = format!("{}={}", name, value); let putenv_cstring = CString::new(putenv_string).unwrap(); let putenv_raw_ptr = putenv_cstring.as_ptr(); @@ -45,7 +47,8 @@ pub fn _setenv(ctx: &EmEnv, name: u32, value: u32, _overwrite: u32) -> c_int { /// emscripten: _putenv // (name: *const char); pub fn _putenv(ctx: &EmEnv, name: c_int) -> c_int { debug!("emscripten::_putenv"); - let name_addr = emscripten_memory_pointer!(ctx.memory(0), name) as *const c_char; + let memory = ctx.memory(0); + let name_addr = emscripten_memory_pointer!(&memory, name) as *const c_char; debug!("=> name({:?})", unsafe { std::ffi::CStr::from_ptr(name_addr) }); @@ -55,7 +58,8 @@ pub fn _putenv(ctx: &EmEnv, name: c_int) -> c_int { /// emscripten: _unsetenv // (name: *const char); pub fn _unsetenv(ctx: &EmEnv, name: u32) -> c_int { debug!("emscripten::_unsetenv"); - let name = read_string_from_wasm(ctx.memory(0), name); + let memory = ctx.memory(0); + let name = read_string_from_wasm(&memory, name); // no unsetenv on windows, so use putenv with an empty value let unsetenv_string = format!("{}=", name); let unsetenv_cstring = CString::new(unsetenv_string).unwrap(); @@ -69,6 +73,7 @@ pub fn _getpwnam(ctx: &EmEnv, name_ptr: c_int) -> c_int { debug!("emscripten::_getpwnam {}", name_ptr); #[cfg(not(feature = "debug"))] let _ = name_ptr; + let memory = ctx.memory(0); #[repr(C)] struct GuestPasswd { @@ -85,7 +90,7 @@ pub fn _getpwnam(ctx: &EmEnv, name_ptr: c_int) -> c_int { unsafe { let passwd_struct_offset = call_malloc(ctx, mem::size_of::() as _); let passwd_struct_ptr = - emscripten_memory_pointer!(ctx.memory(0), passwd_struct_offset) as *mut GuestPasswd; + emscripten_memory_pointer!(&memory, passwd_struct_offset) as *mut GuestPasswd; (*passwd_struct_ptr).pw_name = 0; (*passwd_struct_ptr).pw_passwd = 0; (*passwd_struct_ptr).pw_gecos = 0; @@ -103,6 +108,7 @@ pub fn _getgrnam(ctx: &EmEnv, name_ptr: c_int) -> c_int { debug!("emscripten::_getgrnam {}", name_ptr); #[cfg(not(feature = "debug"))] let _ = name_ptr; + let memory = ctx.memory(0); #[repr(C)] struct GuestGroup { @@ -116,7 +122,7 @@ pub fn _getgrnam(ctx: &EmEnv, name_ptr: c_int) -> c_int { unsafe { let group_struct_offset = call_malloc(ctx, mem::size_of::() as _); let group_struct_ptr = - emscripten_memory_pointer!(ctx.memory(0), group_struct_offset) as *mut GuestGroup; + emscripten_memory_pointer!(&memory, group_struct_offset) as *mut GuestGroup; (*group_struct_ptr).gr_name = 0; (*group_struct_ptr).gr_passwd = 0; (*group_struct_ptr).gr_gid = 0; diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index 5afe48b11..0785627d8 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -17,11 +17,11 @@ use lazy_static::lazy_static; use std::collections::HashMap; use std::f64; use std::path::PathBuf; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use wasmer::{ - imports, namespace, Exports, ExternRef, Function, FunctionType, Global, ImportObject, Instance, - LazyInit, Memory, MemoryType, Module, NativeFunc, Pages, RuntimeError, Store, Table, TableType, - Val, ValType, WasmerEnv, + imports, namespace, Exports, Function, FunctionType, Global, ImportObject, Instance, LazyInit, + Memory, MemoryType, Module, NativeFunc, Pages, RuntimeError, Store, Table, TableType, Val, + ValType, WasmerEnv, }; #[cfg(unix)] @@ -71,7 +71,7 @@ pub use self::utils::{ #[derive(Clone)] /// The environment provided to the Emscripten imports. pub struct EmEnv { - memory: Arc>, + memory: Arc>>, data: Arc>, } @@ -86,21 +86,19 @@ impl WasmerEnv for EmEnv { impl EmEnv { pub fn new(data: &EmscriptenGlobalsData, mapped_dirs: HashMap) -> Self { Self { - memory: Arc::new(None), + memory: Arc::new(RwLock::new(None)), data: Arc::new(Mutex::new(EmscriptenData::new(data.clone(), mapped_dirs))), } } pub fn set_memory(&mut self, memory: Memory) { - let ptr = Arc::as_ptr(&self.memory) as *mut _; - unsafe { - *ptr = Some(memory); - } + let mut w = self.memory.write().unwrap(); + *w = Some(memory); } /// Get a reference to the memory - pub fn memory(&self, _mem_idx: u32) -> &Memory { - (*self.memory).as_ref().unwrap() + pub fn memory(&self, _mem_idx: u32) -> Memory { + (&*self.memory.read().unwrap()).as_ref().cloned().unwrap() } } @@ -324,10 +322,10 @@ pub fn emscripten_call_main( ) -> Result<(), RuntimeError> { let (function_name, main_func) = match instance.exports.get::("_main") { Ok(func) => Ok(("_main", func)), - Err(_e) => match instance.exports.get::("main") { - Ok(func) => Ok(("main", func)), - Err(e) => Err(e), - }, + Err(_e) => instance + .exports + .get::("main") + .map(|func| ("main", func)), } .map_err(|e| RuntimeError::new(e.to_string()))?; let num_params = main_func.ty().params().len(); @@ -477,8 +475,7 @@ impl EmscriptenGlobals { minimum: table_min, maximum: table_max, }; - // TODO: review init value - let table = Table::new(store, table_type, Val::ExternRef(ExternRef::null())).unwrap(); + let table = Table::new(store, table_type, Val::FuncRef(None)).unwrap(); let data = { let static_bump = STATIC_BUMP; diff --git a/lib/emscripten/src/syscalls/mod.rs b/lib/emscripten/src/syscalls/mod.rs index 1922e2496..5210da764 100644 --- a/lib/emscripten/src/syscalls/mod.rs +++ b/lib/emscripten/src/syscalls/mod.rs @@ -356,8 +356,9 @@ pub fn ___syscall183(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> i32 { let path = get_current_directory(ctx); let path_string = path.unwrap().display().to_string(); let len = path_string.len(); + let memory = ctx.memory(0); - let buf_writer = buf_offset.deref(ctx.memory(0), 0, len as u32 + 1).unwrap(); + let buf_writer = buf_offset.deref(&memory, 0, len as u32 + 1).unwrap(); for (i, byte) in path_string.bytes().enumerate() { buf_writer[i].set(byte as _); } @@ -411,8 +412,9 @@ pub fn ___syscall140(ctx: &EmEnv, _which: i32, mut varargs: VarArgs) -> i32 { let whence: i32 = varargs.get(ctx); let offset = offset_low; let ret = unsafe { lseek(fd, offset as _, whence) as i64 }; + let memory = ctx.memory(0); - let result_ptr = result_ptr_value.deref(ctx.memory(0)).unwrap(); + let result_ptr = result_ptr_value.deref(&memory).unwrap(); result_ptr.set(ret); debug!( diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index cf39590ed..69ac5a2f4 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -515,6 +515,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int debug!("emscripten::___syscall102 (socketcall) {}", _which); let call: u32 = varargs.get(ctx); let mut socket_varargs: VarArgs = varargs.get(ctx); + let memory = ctx.memory(0); // migrating to EmSockAddr, port being separate here is nice, should update that too #[repr(C)] @@ -579,7 +580,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket = socket_varargs.get(ctx); let address: u32 = socket_varargs.get(ctx); let address_len = socket_varargs.get(ctx); - let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; + let address = emscripten_memory_pointer!(&memory, address) as *mut sockaddr; // Debug received address let _proper_address = address as *const GuestSockaddrIn; @@ -604,7 +605,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket = socket_varargs.get(ctx); let address: u32 = socket_varargs.get(ctx); let address_len = socket_varargs.get(ctx); - let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; + let address = emscripten_memory_pointer!(&memory, address) as *mut sockaddr; unsafe { connect(socket, address, address_len) } } 4 => { @@ -629,11 +630,10 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int debug!( "=> socket: {}, address: {:?}, address_len: {}", socket, - address.deref(ctx.memory(0)).unwrap().get(), - address_len.deref(ctx.memory(0)).unwrap().get() + address.deref(&memory).unwrap().get(), + address_len.deref(&memory).unwrap().get() ); - let address_len_addr = - unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() }; + let address_len_addr = unsafe { address_len.deref_mut(&memory).unwrap().get_mut() }; // let mut address_len_addr: socklen_t = 0; let mut host_address: sockaddr = sockaddr { @@ -643,7 +643,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int sa_len: Default::default(), }; let fd = unsafe { accept(socket, &mut host_address, address_len_addr) }; - let address_addr = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() }; + let address_addr = unsafe { address.deref_mut(&memory).unwrap().get_mut() }; address_addr.sa_family = host_address.sa_family as _; address_addr.sa_data = host_address.sa_data; @@ -667,8 +667,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket: i32 = socket_varargs.get(ctx); let address: WasmPtr = socket_varargs.get(ctx); let address_len: WasmPtr = socket_varargs.get(ctx); - let address_len_addr = - unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() }; + let address_len_addr = unsafe { address_len.deref_mut(&memory).unwrap().get_mut() }; let mut sock_addr_host: sockaddr = sockaddr { sa_family: Default::default(), @@ -684,7 +683,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int ) }; // translate from host data into emscripten data - let mut address_mut = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() }; + let mut address_mut = unsafe { address.deref_mut(&memory).unwrap().get_mut() }; address_mut.sa_family = sock_addr_host.sa_family as _; address_mut.sa_data = sock_addr_host.sa_data; @@ -701,9 +700,9 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket = socket_varargs.get(ctx); let address: u32 = socket_varargs.get(ctx); let address_len: u32 = socket_varargs.get(ctx); - let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; + let address = emscripten_memory_pointer!(memory, address) as *mut sockaddr; let address_len_addr = - emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut socklen_t; + emscripten_memory_pointer!(memory, address_len) as *mut socklen_t; unsafe { getpeername(socket, address, address_len_addr) } } 11 => { @@ -715,8 +714,8 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let len: i32 = socket_varargs.get(ctx); let address: u32 = socket_varargs.get(ctx); let address_len = socket_varargs.get(ctx); - let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as _; - let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; + let buf_addr = emscripten_memory_pointer!(memory, buf) as _; + let address = emscripten_memory_pointer!(memory, address) as *mut sockaddr; unsafe { sendto(socket, buf_addr, flags, len, address, address_len) as i32 } } 12 => { @@ -728,10 +727,10 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let flags: i32 = socket_varargs.get(ctx); let address: u32 = socket_varargs.get(ctx); let address_len: u32 = socket_varargs.get(ctx); - let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as _; - let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; + let buf_addr = emscripten_memory_pointer!(memory, buf) as _; + let address = emscripten_memory_pointer!(memory, address) as *mut sockaddr; let address_len_addr = - emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut socklen_t; + emscripten_memory_pointer!(memory, address_len) as *mut socklen_t; unsafe { recvfrom( socket, @@ -755,8 +754,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let untranslated_name: i32 = socket_varargs.get(ctx); let value: u32 = socket_varargs.get(ctx); let option_len: u32 = socket_varargs.get(ctx); - let value_addr = - emscripten_memory_pointer!(ctx.memory(0), value) as *const libc::c_void; + let value_addr = emscripten_memory_pointer!(memory, value) as *const libc::c_void; let name: i32 = translate_socket_name_flag(untranslated_name); let ret = unsafe { setsockopt(socket, level, name, value_addr, option_len) }; @@ -774,9 +772,8 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let name: i32 = translate_socket_name_flag(untranslated_name); let value: u32 = socket_varargs.get(ctx); let option_len: u32 = socket_varargs.get(ctx); - let value_addr = emscripten_memory_pointer!(ctx.memory(0), value) as _; - let option_len_addr = - emscripten_memory_pointer!(ctx.memory(0), option_len) as *mut socklen_t; + let value_addr = emscripten_memory_pointer!(memory, value) as _; + let option_len_addr = emscripten_memory_pointer!(memory, option_len) as *mut socklen_t; unsafe { getsockopt(socket, level, name, value_addr, option_len_addr) } } 16 => { @@ -785,7 +782,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket: i32 = socket_varargs.get(ctx); let msg: u32 = socket_varargs.get(ctx); let flags: i32 = socket_varargs.get(ctx); - let msg_addr = emscripten_memory_pointer!(ctx.memory(0), msg) as *const msghdr; + let msg_addr = emscripten_memory_pointer!(memory, msg) as *const msghdr; unsafe { sendmsg(socket, msg_addr, flags) as i32 } } 17 => { @@ -794,7 +791,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let socket: i32 = socket_varargs.get(ctx); let msg: u32 = socket_varargs.get(ctx); let flags: i32 = socket_varargs.get(ctx); - let msg_addr = emscripten_memory_pointer!(ctx.memory(0), msg) as *mut msghdr; + let msg_addr = emscripten_memory_pointer!(memory, msg) as *mut msghdr; unsafe { recvmsg(socket, msg_addr, flags) as i32 } } _ => { @@ -858,8 +855,9 @@ pub fn ___syscall168(ctx: &EmEnv, _which: i32, mut varargs: VarArgs) -> i32 { let fds: WasmPtr = varargs.get(ctx); let nfds: u32 = varargs.get(ctx); let timeout: i32 = varargs.get(ctx); + let memory = ctx.memory(0); - let fds_mut = unsafe { fds.deref_mut(ctx.memory(0)).unwrap().get_mut() }; + let fds_mut = unsafe { fds.deref_mut(&memory).unwrap().get_mut() }; let ret = unsafe { libc::poll( diff --git a/lib/emscripten/src/syscalls/windows.rs b/lib/emscripten/src/syscalls/windows.rs index abd2ad94a..169b5f836 100644 --- a/lib/emscripten/src/syscalls/windows.rs +++ b/lib/emscripten/src/syscalls/windows.rs @@ -27,6 +27,8 @@ pub fn ___syscall5(ctx: &EmEnv, which: c_int, mut varargs: VarArgs) -> c_int { let flags: i32 = varargs.get(ctx); let mode: u32 = varargs.get(ctx); let path_str = unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() }; + let memory = ctx.memory(0); + match path_str { "/dev/urandom" => { // create a fake urandom file for windows, super hacky @@ -44,7 +46,7 @@ pub fn ___syscall5(ctx: &EmEnv, which: c_int, mut varargs: VarArgs) -> c_int { // put the file path string into wasm memory let urandom_file_offset = unsafe { copy_cstr_into_wasm(ctx, ptr) }; let raw_pointer_to_urandom_file = - emscripten_memory_pointer!(ctx.memory(0), urandom_file_offset) as *const i8; + emscripten_memory_pointer!(&memory, urandom_file_offset) as *const i8; let fd = unsafe { open(raw_pointer_to_urandom_file, flags, mode) }; debug!( "=> pathname: {}, flags: {}, mode: {} = fd: {}", diff --git a/lib/engine-jit/src/artifact.rs b/lib/engine-jit/src/artifact.rs index d0a548bb8..a83e026e5 100644 --- a/lib/engine-jit/src/artifact.rs +++ b/lib/engine-jit/src/artifact.rs @@ -23,7 +23,8 @@ use wasmer_types::{ TableIndex, }; use wasmer_vm::{ - FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMSharedSignatureIndex, VMTrampoline, + FuncDataRegistry, FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMSharedSignatureIndex, + VMTrampoline, }; /// A compiled wasm module, ready to be instantiated. @@ -35,6 +36,7 @@ pub struct JITArtifact { finished_function_call_trampolines: BoxedSlice, finished_dynamic_function_trampolines: BoxedSlice, signatures: BoxedSlice, + func_data_registry: Arc, frame_info_registration: Mutex>, finished_function_lengths: BoxedSlice, } @@ -226,6 +228,7 @@ impl JITArtifact { let finished_dynamic_function_trampolines = finished_dynamic_function_trampolines.into_boxed_slice(); let signatures = signatures.into_boxed_slice(); + let func_data_registry = inner_jit.func_data().clone(); Ok(Self { serializable, @@ -235,6 +238,7 @@ impl JITArtifact { signatures, frame_info_registration: Mutex::new(None), finished_function_lengths, + func_data_registry, }) } @@ -314,6 +318,9 @@ impl Artifact for JITArtifact { &self.signatures } + fn func_data_registry(&self) -> &FuncDataRegistry { + &self.func_data_registry + } fn serialize(&self) -> Result, SerializeError> { // let mut s = flexbuffers::FlexbufferSerializer::new(); // self.serializable.serialize(&mut s).map_err(|e| SerializeError::Generic(format!("{:?}", e))); diff --git a/lib/engine-jit/src/engine.rs b/lib/engine-jit/src/engine.rs index 9009d937b..b089bb1ff 100644 --- a/lib/engine-jit/src/engine.rs +++ b/lib/engine-jit/src/engine.rs @@ -13,8 +13,8 @@ use wasmer_types::entity::PrimaryMap; use wasmer_types::Features; use wasmer_types::{FunctionIndex, FunctionType, LocalFunctionIndex, SignatureIndex}; use wasmer_vm::{ - FunctionBodyPtr, ModuleInfo, SectionBodyPtr, SignatureRegistry, VMFunctionBody, - VMSharedSignatureIndex, VMTrampoline, + FuncDataRegistry, FunctionBodyPtr, ModuleInfo, SectionBodyPtr, SignatureRegistry, + VMCallerCheckedAnyfunc, VMFuncRef, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline, }; /// A WebAssembly `JIT` Engine. @@ -35,6 +35,7 @@ impl JITEngine { compiler: Some(compiler), code_memory: vec![], signatures: SignatureRegistry::new(), + func_data: Arc::new(FuncDataRegistry::new()), features, })), target: Arc::new(target), @@ -62,6 +63,7 @@ impl JITEngine { compiler: None, code_memory: vec![], signatures: SignatureRegistry::new(), + func_data: Arc::new(FuncDataRegistry::new()), features: Features::default(), })), target: Arc::new(Target::default()), @@ -90,6 +92,11 @@ impl Engine for JITEngine { compiler.signatures().register(func_type) } + fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef { + let compiler = self.inner(); + compiler.func_data().register(func_data) + } + /// Lookup a signature fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option { let compiler = self.inner(); @@ -152,6 +159,10 @@ pub struct JITEngineInner { /// The signature registry is used mainly to operate with trampolines /// performantly. signatures: SignatureRegistry, + /// The backing storage of `VMFuncRef`s. This centralized store ensures that 2 + /// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`. + /// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped. + func_data: Arc, } impl JITEngineInner { @@ -299,4 +310,9 @@ impl JITEngineInner { pub fn signatures(&self) -> &SignatureRegistry { &self.signatures } + + /// Shared func metadata registry. + pub(crate) fn func_data(&self) -> &Arc { + &self.func_data + } } diff --git a/lib/engine-native/src/artifact.rs b/lib/engine-native/src/artifact.rs index 33483250f..d82056d0b 100644 --- a/lib/engine-native/src/artifact.rs +++ b/lib/engine-native/src/artifact.rs @@ -33,8 +33,8 @@ use wasmer_types::{ TableIndex, }; use wasmer_vm::{ - FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMFunctionBody, VMSharedSignatureIndex, - VMTrampoline, + FuncDataRegistry, FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMFunctionBody, + VMSharedSignatureIndex, VMTrampoline, }; /// A compiled wasm module, ready to be instantiated. @@ -46,6 +46,7 @@ pub struct NativeArtifact { #[loupe(skip)] finished_function_call_trampolines: BoxedSlice, finished_dynamic_function_trampolines: BoxedSlice, + func_data_registry: Arc, signatures: BoxedSlice, } @@ -356,6 +357,7 @@ impl NativeArtifact { .into_boxed_slice(), finished_dynamic_function_trampolines: finished_dynamic_function_trampolines .into_boxed_slice(), + func_data_registry: Arc::new(FuncDataRegistry::new()), signatures: signatures.into_boxed_slice(), }) } @@ -459,6 +461,7 @@ impl NativeArtifact { .into_boxed_slice(), finished_dynamic_function_trampolines: finished_dynamic_function_trampolines .into_boxed_slice(), + func_data_registry: engine_inner.func_data().clone(), signatures: signatures.into_boxed_slice(), }) } @@ -613,6 +616,10 @@ impl Artifact for NativeArtifact { &self.signatures } + fn func_data_registry(&self) -> &FuncDataRegistry { + &self.func_data_registry + } + fn preinstantiate(&self) -> Result<(), InstantiationError> { Ok(()) } diff --git a/lib/engine-native/src/engine.rs b/lib/engine-native/src/engine.rs index 84f857fd6..196f743a0 100644 --- a/lib/engine-native/src/engine.rs +++ b/lib/engine-native/src/engine.rs @@ -13,7 +13,9 @@ use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables}; #[cfg(feature = "compiler")] use wasmer_types::Features; use wasmer_types::FunctionType; -use wasmer_vm::{SignatureRegistry, VMSharedSignatureIndex}; +use wasmer_vm::{ + FuncDataRegistry, SignatureRegistry, VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex, +}; /// A WebAssembly `Native` Engine. #[derive(Clone, MemoryUsage)] @@ -35,6 +37,7 @@ impl NativeEngine { inner: Arc::new(Mutex::new(NativeEngineInner { compiler: Some(compiler), signatures: SignatureRegistry::new(), + func_data: Arc::new(FuncDataRegistry::new()), prefixer: None, features, is_cross_compiling, @@ -67,6 +70,7 @@ impl NativeEngine { #[cfg(feature = "compiler")] features: Features::default(), signatures: SignatureRegistry::new(), + func_data: Arc::new(FuncDataRegistry::new()), prefixer: None, is_cross_compiling: false, linker: Linker::None, @@ -116,6 +120,11 @@ impl Engine for NativeEngine { compiler.signatures().register(func_type) } + fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef { + let compiler = self.inner(); + compiler.func_data().register(func_data) + } + /// Lookup a signature fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option { let compiler = self.inner(); @@ -234,6 +243,11 @@ pub struct NativeEngineInner { /// performantly. signatures: SignatureRegistry, + /// The backing storage of `VMFuncRef`s. This centralized store ensures that 2 + /// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`. + /// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped. + func_data: Arc, + /// The prefixer returns the a String to prefix each of /// the functions in the shared object generated by the `NativeEngine`, /// so we can assure no collisions. @@ -297,6 +311,11 @@ impl NativeEngineInner { &self.signatures } + /// Shared func metadata registry. + pub(crate) fn func_data(&self) -> &Arc { + &self.func_data + } + pub(crate) fn is_cross_compiling(&self) -> bool { self.is_cross_compiling } diff --git a/lib/engine-object-file/src/artifact.rs b/lib/engine-object-file/src/artifact.rs index 122cca040..9b5b384de 100644 --- a/lib/engine-object-file/src/artifact.rs +++ b/lib/engine-object-file/src/artifact.rs @@ -27,7 +27,8 @@ use wasmer_types::{ TableIndex, }; use wasmer_vm::{ - FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMSharedSignatureIndex, VMTrampoline, + FuncDataRegistry, FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMSharedSignatureIndex, + VMTrampoline, }; /// A compiled wasm module, ready to be instantiated. @@ -40,6 +41,7 @@ pub struct ObjectFileArtifact { finished_function_call_trampolines: BoxedSlice, finished_dynamic_function_trampolines: BoxedSlice, signatures: BoxedSlice, + func_data_registry: Arc, /// Length of the serialized metadata metadata_length: usize, symbol_registry: ModuleMetadataSymbolRegistry, @@ -278,6 +280,7 @@ impl ObjectFileArtifact { finished_dynamic_function_trampolines: finished_dynamic_function_trampolines .into_boxed_slice(), signatures: signatures.into_boxed_slice(), + func_data_registry: engine_inner.func_data().clone(), metadata_length, symbol_registry, }) @@ -317,11 +320,22 @@ impl ObjectFileArtifact { let engine_inner = engine.inner(); let signature_registry = engine_inner.signatures(); + let func_data_registry = engine_inner.func_data().clone(); let mut sig_map: BTreeMap = BTreeMap::new(); + let num_imported_functions = metadata.compile_info.module.num_imported_functions; + // set up the imported functions first... + for i in 0..num_imported_functions { + let sig_idx = metadata.compile_info.module.functions[FunctionIndex::new(i)]; + let func_type = &metadata.compile_info.module.signatures[sig_idx]; + let vm_shared_idx = signature_registry.register(&func_type); + sig_map.insert(sig_idx, vm_shared_idx); + } // read finished functions in order now... for i in 0..num_finished_functions { - let sig_idx = metadata.compile_info.module.functions[FunctionIndex::new(i)]; + let local_func_idx = LocalFunctionIndex::new(i); + let func_idx = metadata.compile_info.module.func_index(local_func_idx); + let sig_idx = metadata.compile_info.module.functions[func_idx]; let func_type = &metadata.compile_info.module.signatures[sig_idx]; let vm_shared_idx = signature_registry.register(&func_type); sig_map.insert(sig_idx, vm_shared_idx); @@ -386,6 +400,7 @@ impl ObjectFileArtifact { finished_dynamic_function_trampolines: finished_dynamic_function_trampolines .into_boxed_slice(), signatures: signatures.into_boxed_slice(), + func_data_registry, metadata_length: 0, symbol_registry, }) @@ -451,6 +466,10 @@ impl Artifact for ObjectFileArtifact { &self.signatures } + fn func_data_registry(&self) -> &FuncDataRegistry { + &self.func_data_registry + } + fn preinstantiate(&self) -> Result<(), InstantiationError> { Ok(()) } diff --git a/lib/engine-object-file/src/engine.rs b/lib/engine-object-file/src/engine.rs index 8bd589361..2a15137bf 100644 --- a/lib/engine-object-file/src/engine.rs +++ b/lib/engine-object-file/src/engine.rs @@ -10,7 +10,9 @@ use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables}; #[cfg(feature = "compiler")] use wasmer_types::Features; use wasmer_types::FunctionType; -use wasmer_vm::{SignatureRegistry, VMSharedSignatureIndex}; +use wasmer_vm::{ + FuncDataRegistry, SignatureRegistry, VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex, +}; /// A WebAssembly `ObjectFile` Engine. #[derive(Clone, MemoryUsage)] @@ -29,6 +31,7 @@ impl ObjectFileEngine { inner: Arc::new(Mutex::new(ObjectFileEngineInner { compiler: Some(compiler), signatures: SignatureRegistry::new(), + func_data: Arc::new(FuncDataRegistry::new()), prefixer: None, features, })), @@ -58,6 +61,7 @@ impl ObjectFileEngine { #[cfg(feature = "compiler")] features: Features::default(), signatures: SignatureRegistry::new(), + func_data: Arc::new(FuncDataRegistry::new()), prefixer: None, })), target: Arc::new(Target::default()), @@ -104,6 +108,11 @@ impl Engine for ObjectFileEngine { compiler.signatures().register(func_type) } + fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef { + let compiler = self.inner(); + compiler.func_data().register(func_data) + } + /// Lookup a signature fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option { let compiler = self.inner(); @@ -180,6 +189,11 @@ pub struct ObjectFileEngineInner { /// performantly. signatures: SignatureRegistry, + /// The backing storage of `VMFuncRef`s. This centralized store ensures that 2 + /// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`. + /// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped. + func_data: Arc, + /// The prefixer returns the a String to prefix each of /// the functions in the shared object generated by the `ObjectFileEngine`, /// so we can assure no collisions. @@ -232,4 +246,9 @@ impl ObjectFileEngineInner { pub fn signatures(&self) -> &SignatureRegistry { &self.signatures } + + /// Shared func metadata registry. + pub(crate) fn func_data(&self) -> &Arc { + &self.func_data + } } diff --git a/lib/engine/src/artifact.rs b/lib/engine/src/artifact.rs index db420f4f9..a706dbdd7 100644 --- a/lib/engine/src/artifact.rs +++ b/lib/engine/src/artifact.rs @@ -13,8 +13,8 @@ use wasmer_types::{ SignatureIndex, TableIndex, }; use wasmer_vm::{ - FunctionBodyPtr, InstanceAllocator, InstanceHandle, MemoryStyle, ModuleInfo, TableStyle, - VMSharedSignatureIndex, VMTrampoline, + FuncDataRegistry, FunctionBodyPtr, InstanceAllocator, InstanceHandle, MemoryStyle, ModuleInfo, + TableStyle, VMSharedSignatureIndex, VMTrampoline, }; /// An `Artifact` is the product that the `Engine` @@ -67,6 +67,9 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage { /// Returns the associated VM signatures for this `Artifact`. fn signatures(&self) -> &BoxedSlice; + /// Get the func data registry + fn func_data_registry(&self) -> &FuncDataRegistry; + /// Serializes an artifact into bytes fn serialize(&self) -> Result, SerializeError>; @@ -143,6 +146,7 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage { finished_globals, imports, self.signatures().clone(), + self.func_data_registry(), host_state, import_function_envs, ) diff --git a/lib/engine/src/engine.rs b/lib/engine/src/engine.rs index 8d5f6c360..b321c5c4b 100644 --- a/lib/engine/src/engine.rs +++ b/lib/engine/src/engine.rs @@ -9,7 +9,7 @@ use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use std::sync::Arc; use wasmer_compiler::{CompileError, Target}; use wasmer_types::FunctionType; -use wasmer_vm::VMSharedSignatureIndex; +use wasmer_vm::{VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex}; /// A unimplemented Wasmer `Engine`. /// @@ -24,6 +24,9 @@ pub trait Engine: MemoryUsage { /// Register a signature fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex; + /// Register a function's data. + fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef; + /// Lookup a signature fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option; diff --git a/lib/types/Cargo.toml b/lib/types/Cargo.toml index 338f6bc9b..3a86972ea 100644 --- a/lib/types/Cargo.toml +++ b/lib/types/Cargo.toml @@ -23,3 +23,6 @@ std = ["serde/std"] core = [] enable-rkyv = ["rkyv"] enable-serde = ["serde"] + +# experimental / in-development features +experimental-reference-types-extern-ref = [] diff --git a/lib/types/src/extern_ref.rs b/lib/types/src/extern_ref.rs new file mode 100644 index 000000000..85494baf6 --- /dev/null +++ b/lib/types/src/extern_ref.rs @@ -0,0 +1,295 @@ +use std::any::Any; +use std::ptr; +use std::sync::atomic; + +/// This type does not do reference counting automatically, reference counting can be done with +/// [`Self::ref_clone`] and [`Self::ref_drop`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct VMExternRef(*const VMExternRefInner); + +impl VMExternRef { + /// The maximum number of references allowed to this data. + const MAX_REFCOUNT: usize = std::usize::MAX - 1; + + /// Checks if the given ExternRef is null. + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + /// New null extern ref + pub const fn null() -> Self { + Self(ptr::null()) + } + + /// Get a bit-level representation of an externref. + /// For internal use for packing / unpacking it for calling functions. + pub(crate) fn to_binary(self) -> i128 { + self.0 as i128 + } + + /// Create an externref from bit-level representation. + /// For internal use for packing / unpacking it for calling functions. + /// + /// # Safety + /// The pointer is assumed valid or null. Passing arbitrary data to this function will + /// result in undefined behavior. It is the caller's responsibility to verify that only + /// valid externref bit patterns are passed to this function. + pub(crate) unsafe fn from_binary(bits: i128) -> Self { + Self(bits as usize as *const _) + } + + /// Make a new extern reference + pub fn new(value: T) -> Self + where + T: Any + Send + Sync + 'static + Sized, + { + Self(Box::into_raw(Box::new(VMExternRefInner::new::(value)))) + } + + /// Try to downcast to the given value + pub fn downcast(&self) -> Option<&T> + where + T: Any + Send + Sync + 'static + Sized, + { + if self.is_null() { + return None; + } + unsafe { + let inner = &*self.0; + + inner.data.downcast_ref::() + } + } + + /// Panic if the ref count gets too high. + #[track_caller] + fn sanity_check_ref_count(old_size: usize, growth_amount: usize) { + // If we exceed 18_446_744_073_709_551_614 references on a 64bit system (or + // 2_147_483_646 references on a 32bit system) then we either live in a future with + // magic technology or we have a bug in our ref counting logic (i.e. a leak). + // Either way, the best course of action is to terminate the program and update + // some code on our side. + // + // Note to future readers: exceeding `usize` ref count is trivially provable as a + // bug on systems that can address `usize` sized memory blocks or smaller because + // the reference itself is at least `usize` in size and all virtual memory would be + // taken by references to the data leaving no room for the data itself. + if old_size + .checked_add(growth_amount) + .map(|v| v > Self::MAX_REFCOUNT) + .unwrap_or(true) + { + panic!("Too many references to `ExternRef`"); + } + } + + /// A low-level function to increment the strong-count a given number of times. + /// + /// This is used as an optimization when implementing some low-level VM primitives. + /// If you're using this type directly for whatever reason, you probably want + /// [`Self::ref_clone`] instead. + pub fn ref_inc_by(&self, val: usize) { + if self.0.is_null() { + return; + } + + let old_size = unsafe { + let ref_inner = &*self.0; + ref_inner.increment_ref_count(val) + }; + + Self::sanity_check_ref_count(old_size, val); + } + + /// A deep copy of the reference, increments the strong count. + pub fn ref_clone(&self) -> Self { + if self.0.is_null() { + return Self(self.0); + } + + let old_size = unsafe { + let ref_inner = &*self.0; + ref_inner.increment_ref_count(1) + }; + + // See comments in [`Self::sanity_check_ref_count`] for more information. + if old_size > Self::MAX_REFCOUNT { + panic!("Too many references to `ExternRef`"); + } + + Self(self.0) + } + + /// Does an inner drop, decrementing the strong count + pub fn ref_drop(&mut self) { + if !self.0.is_null() { + unsafe { + let should_drop = { + let ref_inner: &VMExternRefInner = &*self.0; + ref_inner.decrement_and_drop() + }; + if should_drop { + let _ = Box::from_raw(self.0 as *mut VMExternRefInner); + } + } + } + } + + #[allow(dead_code)] + /// Get the number of strong references to this data. + fn strong_count(&self) -> usize { + if self.0.is_null() { + 0 + } else { + unsafe { (&*self.0).strong.load(atomic::Ordering::SeqCst) } + } + } +} + +#[derive(Debug)] +#[repr(C)] +pub(crate) struct VMExternRefInner { + strong: atomic::AtomicUsize, + /// Do something obviously correct to get started. This can "easily" be improved + /// to be an inline allocation later as the logic is fully encapsulated. + data: Box, +} + +impl VMExternRefInner { + fn new(value: T) -> Self + where + T: Any + Send + Sync + Sized + 'static, + { + Self { + strong: atomic::AtomicUsize::new(1), + data: Box::new(value), + } + } + + /// Increments the reference count. + /// Returns the old value. + fn increment_ref_count(&self, val: usize) -> usize { + // Using a relaxed ordering is alright here, as knowledge of + // the original reference prevents other threads from + // erroneously deleting the object. + // + // As explained in the [Boost documentation][1]: + // + // > Increasing the reference counter can always be done with + // > `memory_order_relaxed`: New references to an object can + // > only be formed from an existing reference, and passing an + // > existing reference from one thread to another must already + // > provide any required synchronization. + // + // [1]: https://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html + self.strong.fetch_add(val, atomic::Ordering::Relaxed) + } + + /// Decrement the count and drop the data if the count hits 0 + /// returns `true` if the containing allocation should be dropped + fn decrement_and_drop(&self) -> bool { + // Because `fetch_sub` is already atomic, we do not need to + // synchronize with other thread. + if self.strong.fetch_sub(1, atomic::Ordering::Release) != 1 { + return false; + } + + // This fence is needed to prevent reordering of use of the data and + // deletion of the data. Because it is marked `Release`, the decreasing + // of the reference count synchronizes with this `Acquire` fence. This + // means that use of the data happens before decreasing the reference + // count, which happens before this fence, which happens before the + // deletion of the data. + // + // As explained in the [Boost documentation][1]: + // + // > It is important to enforce any possible access to the object in one + // > thread (through an existing reference) to *happen before* deleting + // > the object in a different thread. This is achieved by a "release" + // > operation after dropping a reference (any access to the object + // > through this reference must obviously happened before), and an + // > "acquire" operation before deleting the object. + // + // [1]: https://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html + atomic::fence(atomic::Ordering::Acquire); + + return true; + } +} + +#[derive(Debug, PartialEq, Eq)] +#[repr(transparent)] +/// An opaque reference to some data. This reference can be passed through Wasm. +pub struct ExternRef { + inner: VMExternRef, +} + +impl Clone for ExternRef { + fn clone(&self) -> Self { + Self { + inner: self.inner.ref_clone(), + } + } +} + +impl Drop for ExternRef { + fn drop(&mut self) { + self.inner.ref_drop() + } +} + +impl ExternRef { + /// Checks if the given ExternRef is null. + pub fn is_null(&self) -> bool { + self.inner.is_null() + } + + /// New null extern ref + pub fn null() -> Self { + Self { + inner: VMExternRef::null(), + } + } + + #[cfg(feature = "experimental-reference-types-extern-ref")] + /// Make a new extern reference + pub fn new(value: T) -> Self + where + T: Any + Send + Sync + 'static + Sized, + { + Self { + inner: VMExternRef::new(value), + } + } + + #[cfg(feature = "experimental-reference-types-extern-ref")] + /// Try to downcast to the given value + pub fn downcast(&self) -> Option<&T> + where + T: Any + Send + Sync + 'static + Sized, + { + self.inner.downcast::() + } + + #[cfg(feature = "experimental-reference-types-extern-ref")] + /// Get the number of strong references to this data. + pub fn strong_count(&self) -> usize { + self.inner.strong_count() + } +} + +impl From for ExternRef { + fn from(other: VMExternRef) -> Self { + Self { inner: other } + } +} + +impl From for VMExternRef { + fn from(other: ExternRef) -> Self { + let out = other.inner; + // We want to make this transformation without decrementing the count. + std::mem::forget(other); + out + } +} diff --git a/lib/types/src/features.rs b/lib/types/src/features.rs index 9ddd9eb14..ca779ac88 100644 --- a/lib/types/src/features.rs +++ b/lib/types/src/features.rs @@ -42,7 +42,8 @@ impl Features { pub fn new() -> Self { Self { threads: false, - reference_types: false, + // Reference types should be on by default + reference_types: true, simd: false, // Bulk Memory should be on by default bulk_memory: true, @@ -76,16 +77,14 @@ impl Features { /// Configures whether the WebAssembly reference types proposal will be /// enabled. /// - /// The [WebAssembly reference types proposal][proposal] is not currently - /// fully standardized and is undergoing development. Support for this - /// feature can be enabled through this method for appropriate WebAssembly - /// modules. + /// The [WebAssembly reference types proposal][proposal] is now + /// fully standardized and enabled by default. /// /// This feature gates items such as the `externref` type and multiple tables /// being in a module. Note that enabling the reference types feature will /// also enable the bulk memory feature. /// - /// This is `false` by default. + /// This is `true` by default. /// /// [proposal]: https://github.com/webassembly/reference-types pub fn reference_types(&mut self, enable: bool) -> &mut Self { @@ -119,15 +118,13 @@ impl Features { /// Configures whether the WebAssembly bulk memory operations proposal will /// be enabled. /// - /// The [WebAssembly bulk memory operations proposal][proposal] is not - /// currently fully standardized and is undergoing development. - /// Support for this feature can be enabled through this method for - /// appropriate WebAssembly modules. + /// The [WebAssembly bulk memory operations proposal][proposal] is now + /// fully standardized and enabled by default. /// /// This feature gates items such as the `memory.copy` instruction, passive /// data/table segments, etc, being in a module. /// - /// This is `false` by default. + /// This is `true` by default. /// /// [proposal]: https://github.com/webassembly/bulk-memory-operations pub fn bulk_memory(&mut self, enable: bool) -> &mut Self { @@ -177,10 +174,10 @@ impl Features { self } - /// Configures whether the WebAssembly tail-call proposal will + /// Configures whether the WebAssembly module linking proposal will /// be enabled. /// - /// The [WebAssembly tail-call proposal][proposal] is not + /// The [WebAssembly module linking proposal][proposal] is not /// currently fully standardized and is undergoing development. /// Support for this feature can be enabled through this method for /// appropriate WebAssembly modules. @@ -251,7 +248,7 @@ mod test_features { default, Features { threads: false, - reference_types: false, + reference_types: true, simd: false, bulk_memory: true, multi_value: true, diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 4c9bd19fc..6cd30d2a9 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -56,18 +56,19 @@ pub mod lib { #[cfg(feature = "enable-rkyv")] mod archives; +mod extern_ref; mod features; mod indexes; mod initializers; mod memory_view; mod native; -mod r#ref; mod types; mod units; mod values; /// The entity module, with common helpers for Rust structures pub mod entity; +pub use crate::extern_ref::{ExternRef, VMExternRef}; pub use crate::features::Features; pub use crate::indexes::{ CustomSectionIndex, DataIndex, ElemIndex, ExportIndex, FunctionIndex, GlobalIndex, ImportIndex, @@ -79,11 +80,10 @@ pub use crate::initializers::{ }; pub use crate::memory_view::{Atomically, MemoryView}; pub use crate::native::{NativeWasmType, ValueType}; -pub use crate::r#ref::{ExternRef, HostInfo, HostRef}; pub use crate::units::{ Bytes, PageCountOutOfRange, Pages, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, }; -pub use crate::values::Value; +pub use crate::values::{Value, WasmValueType}; pub use types::{ ExportType, ExternType, FunctionType, GlobalInit, GlobalType, ImportType, MemoryType, Mutability, TableType, Type, V128, diff --git a/lib/types/src/native.rs b/lib/types/src/native.rs index e913027d0..f5759b364 100644 --- a/lib/types/src/native.rs +++ b/lib/types/src/native.rs @@ -1,9 +1,10 @@ //! This module permits to create native functions //! easily in Rust, thanks to its advanced typing system. +use crate::extern_ref::VMExternRef; use crate::lib::std::fmt; use crate::types::Type; -use crate::values::Value; +use crate::values::{Value, WasmValueType}; /// `NativeWasmType` represents a Wasm type that has a direct /// representation on the host (hence the “native” term). @@ -37,10 +38,13 @@ pub trait NativeWasmType: Sized { fn to_binary(self) -> i128; /// Convert self to a `Value`. - fn to_value(self) -> Value { + fn to_value(self) -> Value { let binary = self.to_binary(); + // we need a store, we're just hoping we don't actually use it via funcref + // TODO(reftypes): we need an actual solution here + let hack = 3; - unsafe { Value::read_value_from(&binary, Self::WASM_TYPE) } + unsafe { Value::read_value_from(&hack, &binary, Self::WASM_TYPE) } } /// Convert to self from i128 binary representation. @@ -172,6 +176,32 @@ impl NativeWasmType for u128 { } } +impl NativeWasmType for VMExternRef { + const WASM_TYPE: Type = Type::ExternRef; + type Abi = Self; + + #[inline] + fn from_abi(abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self) -> Self::Abi { + self + } + + #[inline] + fn to_binary(self) -> i128 { + self.to_binary() + } + + #[inline] + fn from_binary(bits: i128) -> Self { + // TODO(reftypes): ensure that the safety invariants are actually upheld here + unsafe { Self::from_binary(bits) } + } +} + #[cfg(test)] mod test_native_type { use super::*; diff --git a/lib/types/src/ref.rs b/lib/types/src/ref.rs deleted file mode 100644 index 508a70a2f..000000000 --- a/lib/types/src/ref.rs +++ /dev/null @@ -1,267 +0,0 @@ -#![allow(missing_docs)] - -use crate::lib::std::any::Any; -use crate::lib::std::boxed::Box; -use crate::lib::std::cell::{self, RefCell}; -use crate::lib::std::fmt; -use crate::lib::std::hash; -use crate::lib::std::rc::{Rc, Weak}; - -pub trait HostInfo { - fn finalize(&mut self) {} -} - -trait InternalRefBase: Any { - fn as_any(&self) -> &dyn Any; - fn host_info(&self) -> Option>>; - fn set_host_info(&self, info: Option>); - fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool; -} - -#[derive(Clone)] -pub struct InternalRef(Rc); - -impl InternalRef { - pub fn is_ref(&self) -> bool { - let r = self.0.as_any(); - Any::is::>(r) - } - pub fn get_ref(&self) -> HostRef { - let r = self.0.as_any(); - r.downcast_ref::>() - .expect("reference is not T type") - .clone() - } -} - -struct AnyAndHostInfo { - any: Box, - host_info: Option>, -} - -impl Drop for AnyAndHostInfo { - fn drop(&mut self) { - if let Some(info) = &mut self.host_info { - info.finalize(); - } - } -} - -#[derive(Clone)] -pub struct OtherRef(Rc>); - -/// Represents an opaque reference to any data within WebAssembly. -#[derive(Clone)] -pub enum ExternRef { - /// A reference to no data. - Null, - /// A reference to data stored internally. - Ref(InternalRef), - /// A reference to data located outside. - Other(OtherRef), -} - -impl hash::Hash for ExternRef { - fn hash(&self, _state: &mut H) {} -} - -impl PartialEq for ExternRef { - fn eq(&self, other: &Self) -> bool { - // The `ExternRef`s are the same if they point to the same value - self.ptr_eq(other) - } -} -impl Eq for ExternRef {} - -impl ExternRef { - /// Creates a new instance of `ExternRef` from `Box`. - pub fn new(data: Box) -> Self { - let info = AnyAndHostInfo { - any: data, - host_info: None, - }; - Self::Other(OtherRef(Rc::new(RefCell::new(info)))) - } - - /// Creates a `Null` reference. - pub fn null() -> Self { - Self::Null - } - - /// Returns the data stored in the reference if available. - /// - /// # Panics - /// - /// Panics if the variant isn't `ExternRef::Other`. - pub fn data(&self) -> cell::Ref> { - match self { - Self::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any), - _ => panic!("expected ExternRef::Other"), - } - } - - /// Returns true if the two `ExternRef`'s point to the same value (not just - /// values that compare as equal). - pub fn ptr_eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::Null, Self::Null) => true, - (Self::Ref(InternalRef(ref a)), Self::Ref(InternalRef(ref b))) => a.ptr_eq(b.as_ref()), - (Self::Other(OtherRef(ref a)), Self::Other(OtherRef(ref b))) => Rc::ptr_eq(a, b), - _ => false, - } - } - - /// Returns a mutable reference to the host information if available. - /// - /// # Panics - /// - /// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`. - pub fn host_info(&self) -> Option>> { - match self { - Self::Null => panic!("null"), - Self::Ref(r) => r.0.host_info(), - Self::Other(r) => { - let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info); - if info.is_none() { - return None; - } - Some(cell::RefMut::map(info, |info| info.as_mut().unwrap())) - } - } - } - - /// Sets the host information for an `ExternRef`. - /// - /// # Panics - /// - /// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`. - pub fn set_host_info(&self, info: Option>) { - match self { - Self::Null => panic!("null"), - Self::Ref(r) => r.0.set_host_info(info), - Self::Other(r) => { - r.0.borrow_mut().host_info = info; - } - } - } -} - -impl fmt::Debug for ExternRef { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Null => write!(f, "null"), - Self::Ref(_) => write!(f, "externref"), - Self::Other(_) => write!(f, "other ref"), - } - } -} - -struct ContentBox { - content: T, - host_info: Option>, - externref_data: Weak, -} - -impl Drop for ContentBox { - fn drop(&mut self) { - if let Some(info) = &mut self.host_info { - info.finalize(); - } - } -} - -/// Represents a piece of data located in the host environment. -pub struct HostRef(Rc>>); - -impl HostRef { - /// Creates a new `HostRef` from `T`. - pub fn new(item: T) -> Self { - let externref_data: Weak = Weak::new(); - let content = ContentBox { - content: item, - host_info: None, - externref_data, - }; - Self(Rc::new(RefCell::new(content))) - } - - /// Immutably borrows the wrapped data. - /// - /// # Panics - /// - /// Panics if the value is currently mutably borrowed. - pub fn borrow(&self) -> cell::Ref { - cell::Ref::map(self.0.borrow(), |b| &b.content) - } - - /// Mutably borrows the wrapped data. - /// - /// # Panics - /// - /// Panics if the `HostRef` is already borrowed. - pub fn borrow_mut(&self) -> cell::RefMut { - cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.content) - } - - /// Returns true if the two `HostRef`'s point to the same value (not just - /// values that compare as equal). - pub fn ptr_eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.0, &other.0) - } - - /// Returns an opaque reference to the wrapped data in the form of - /// an `ExternRef`. - /// - /// # Panics - /// - /// Panics if `HostRef` is already mutably borrowed. - pub fn externref(&self) -> ExternRef { - let r = self.0.borrow_mut().externref_data.upgrade(); - if let Some(r) = r { - return ExternRef::Ref(InternalRef(r)); - } - let externref_data: Rc = Rc::new(self.clone()); - self.0.borrow_mut().externref_data = Rc::downgrade(&externref_data); - ExternRef::Ref(InternalRef(externref_data)) - } -} - -impl InternalRefBase for HostRef { - fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool { - if let Some(other) = other.as_any().downcast_ref() { - self.ptr_eq(other) - } else { - false - } - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn host_info(&self) -> Option>> { - let info = cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.host_info); - if info.is_none() { - return None; - } - Some(cell::RefMut::map(info, |info| info.as_mut().unwrap())) - } - - fn set_host_info(&self, info: Option>) { - self.0.borrow_mut().host_info = info; - } -} - -impl Clone for HostRef { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl fmt::Debug for HostRef { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Ref(")?; - self.0.borrow().content.fmt(f)?; - write!(f, ")") - } -} diff --git a/lib/types/src/types.rs b/lib/types/src/types.rs index 4f84fb456..c246dfc8a 100644 --- a/lib/types/src/types.rs +++ b/lib/types/src/types.rs @@ -5,7 +5,7 @@ use crate::lib::std::format; use crate::lib::std::string::{String, ToString}; use crate::lib::std::vec::Vec; use crate::units::Pages; -use crate::values::Value; +use crate::values::{Value, WasmValueType}; use loupe::{MemoryUsage, MemoryUsageTracker}; #[cfg(feature = "enable-rkyv")] @@ -437,6 +437,9 @@ pub enum GlobalInit { V128Const(V128), /// A `global.get` of another global. GetGlobal(GlobalIndex), + // TODO(reftypes): `ref.null func` and `ref.null extern` seem to be 2 different + // things: we need to handle both. Perhaps this handled in context by the + // global knowing its own type? /// A `ref.null`. RefNullConst, /// A `ref.func `. @@ -447,7 +450,7 @@ impl Eq for GlobalInit {} impl GlobalInit { /// Get the `GlobalInit` from a given `Value` - pub fn from_value(value: Value) -> Self { + pub fn from_value(value: Value) -> Self { match value { Value::I32(i) => Self::I32Const(i), Value::I64(i) => Self::I64Const(i), @@ -457,7 +460,7 @@ impl GlobalInit { } } /// Get the `Value` from the Global init value - pub fn to_value(&self) -> Value { + pub fn to_value(&self) -> Value { match self { Self::I32Const(i) => Value::I32(*i), Self::I64Const(i) => Value::I64(*i), diff --git a/lib/types/src/values.rs b/lib/types/src/values.rs index 5722d3ca6..36772ae1b 100644 --- a/lib/types/src/values.rs +++ b/lib/types/src/values.rs @@ -1,8 +1,8 @@ +use crate::extern_ref::ExternRef; use crate::lib::std::convert::TryFrom; use crate::lib::std::fmt; use crate::lib::std::ptr; use crate::lib::std::string::{String, ToString}; -use crate::r#ref::ExternRef; use crate::types::Type; /// Possible runtime values that a WebAssembly module can either consume or @@ -31,7 +31,7 @@ pub enum Value { ExternRef(ExternRef), /// A first-class reference to a WebAssembly function. - FuncRef(T), + FuncRef(Option), /// A 128-bit number V128(u128), @@ -61,7 +61,32 @@ macro_rules! accessors { )*) } -impl Value { +/// Trait for reading and writing Wasm values into binary for use on the layer +/// between the API and the VM internals, specifically with `wasmer_types::Value`. +pub trait WasmValueType: std::fmt::Debug + 'static { + /// Write the value + unsafe fn write_value_to(&self, p: *mut i128); + + /// read the value + // TODO(reftypes): passing the store as `dyn Any` is a hack to work around the + // structure of our crates. We need to talk about the store in the rest of the + // VM (for example where this method is used) but cannot do so. Fixing this + // may be non-trivial. + unsafe fn read_value_from(store: &dyn std::any::Any, p: *const i128) -> Self; +} + +impl WasmValueType for () { + unsafe fn write_value_to(&self, _p: *mut i128) {} + + unsafe fn read_value_from(_store: &dyn std::any::Any, _p: *const i128) -> Self { + () + } +} + +impl Value +where + T: WasmValueType, +{ /// Returns a null `externref` value. pub fn null() -> Self { Self::ExternRef(ExternRef::null()) @@ -93,7 +118,10 @@ impl Value { Self::F32(u) => ptr::write(p as *mut f32, *u), Self::F64(u) => ptr::write(p as *mut f64, *u), Self::V128(b) => ptr::write(p as *mut u128, *b), - _ => unimplemented!("Value::write_value_to"), + Self::FuncRef(Some(b)) => T::write_value_to(b, p), + Self::FuncRef(None) => ptr::write(p as *mut usize, 0), + // TODO(reftypes): review clone here + Self::ExternRef(extern_ref) => ptr::write(p as *mut ExternRef, extern_ref.clone()), } } @@ -103,14 +131,25 @@ impl Value { /// `p` must be: /// - Properly aligned to the specified `ty`'s Rust equivalent /// - Non-null and pointing to valid memory - pub unsafe fn read_value_from(p: *const i128, ty: Type) -> Self { + pub unsafe fn read_value_from(store: &dyn std::any::Any, p: *const i128, ty: Type) -> Self { match ty { Type::I32 => Self::I32(ptr::read(p as *const i32)), Type::I64 => Self::I64(ptr::read(p as *const i64)), Type::F32 => Self::F32(ptr::read(p as *const f32)), Type::F64 => Self::F64(ptr::read(p as *const f64)), Type::V128 => Self::V128(ptr::read(p as *const u128)), - _ => unimplemented!("Value::read_value_from"), + Type::FuncRef => { + // We do the null check ourselves + if (*(p as *const usize)) == 0 { + Self::FuncRef(None) + } else { + Self::FuncRef(Some(T::read_value_from(store, p))) + } + } + Type::ExternRef => { + let extern_ref = (&*(p as *const ExternRef)).clone(); + Self::ExternRef(extern_ref) + } } } @@ -120,33 +159,16 @@ impl Value { (I64(i64) i64 unwrap_i64 *e) (F32(f32) f32 unwrap_f32 *e) (F64(f64) f64 unwrap_f64 *e) - (FuncRef(&T) funcref unwrap_funcref e) + (ExternRef(ExternRef) externref unwrap_externref e.clone()) + (FuncRef(&Option) funcref unwrap_funcref e) (V128(u128) v128 unwrap_v128 *e) } - - /// Attempt to access the underlying value of this `Value`, returning - /// `None` if it is not the correct type. - /// - /// This will return `Some` for both the `ExternRef` and `FuncRef` types. - pub fn externref(&self) -> Option { - match self { - Self::ExternRef(e) => Some(e.clone()), - _ => None, - } - } - - /// Returns the underlying value of this `Value`, panicking if it's the - /// wrong type. - /// - /// # Panics - /// - /// Panics if `self` is not of the right type. - pub fn unwrap_externref(&self) -> ExternRef { - self.externref().expect("expected externref") - } } -impl fmt::Debug for Value { +impl fmt::Debug for Value +where + T: WasmValueType, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::I32(v) => write!(f, "I32({:?})", v), @@ -154,13 +176,17 @@ impl fmt::Debug for Value { Self::F32(v) => write!(f, "F32({:?})", v), Self::F64(v) => write!(f, "F64({:?})", v), Self::ExternRef(v) => write!(f, "ExternRef({:?})", v), - Self::FuncRef(_) => write!(f, "FuncRef"), + Self::FuncRef(None) => write!(f, "Null FuncRef"), + Self::FuncRef(Some(v)) => write!(f, "FuncRef({:?})", v), Self::V128(v) => write!(f, "V128({:?})", v), } } } -impl ToString for Value { +impl ToString for Value +where + T: WasmValueType, +{ fn to_string(&self) -> String { match self { Self::I32(v) => v.to_string(), @@ -174,45 +200,66 @@ impl ToString for Value { } } -impl From for Value { +impl From for Value +where + T: WasmValueType, +{ fn from(val: i32) -> Self { Self::I32(val) } } -impl From for Value { +impl From for Value +where + T: WasmValueType, +{ fn from(val: u32) -> Self { // In Wasm integers are sign-agnostic, so i32 is basically a 4 byte storage we can use for signed or unsigned 32-bit integers. Self::I32(val as i32) } } -impl From for Value { +impl From for Value +where + T: WasmValueType, +{ fn from(val: i64) -> Self { Self::I64(val) } } -impl From for Value { +impl From for Value +where + T: WasmValueType, +{ fn from(val: u64) -> Self { // In Wasm integers are sign-agnostic, so i64 is basically an 8 byte storage we can use for signed or unsigned 64-bit integers. Self::I64(val as i64) } } -impl From for Value { +impl From for Value +where + T: WasmValueType, +{ fn from(val: f32) -> Self { Self::F32(val) } } -impl From for Value { +impl From for Value +where + T: WasmValueType, +{ fn from(val: f64) -> Self { Self::F64(val) } } -impl From for Value { +impl From for Value +where + T: WasmValueType, +{ fn from(val: ExternRef) -> Self { Self::ExternRef(val) } @@ -229,7 +276,10 @@ const NOT_I64: &str = "Value is not of Wasm type i64"; const NOT_F32: &str = "Value is not of Wasm type f32"; const NOT_F64: &str = "Value is not of Wasm type f64"; -impl TryFrom> for i32 { +impl TryFrom> for i32 +where + T: WasmValueType, +{ type Error = &'static str; fn try_from(value: Value) -> Result { @@ -237,7 +287,10 @@ impl TryFrom> for i32 { } } -impl TryFrom> for u32 { +impl TryFrom> for u32 +where + T: WasmValueType, +{ type Error = &'static str; fn try_from(value: Value) -> Result { @@ -245,7 +298,10 @@ impl TryFrom> for u32 { } } -impl TryFrom> for i64 { +impl TryFrom> for i64 +where + T: WasmValueType, +{ type Error = &'static str; fn try_from(value: Value) -> Result { @@ -253,7 +309,10 @@ impl TryFrom> for i64 { } } -impl TryFrom> for u64 { +impl TryFrom> for u64 +where + T: WasmValueType, +{ type Error = &'static str; fn try_from(value: Value) -> Result { @@ -261,7 +320,10 @@ impl TryFrom> for u64 { } } -impl TryFrom> for f32 { +impl TryFrom> for f32 +where + T: WasmValueType, +{ type Error = &'static str; fn try_from(value: Value) -> Result { @@ -269,7 +331,10 @@ impl TryFrom> for f32 { } } -impl TryFrom> for f64 { +impl TryFrom> for f64 +where + T: WasmValueType, +{ type Error = &'static str; fn try_from(value: Value) -> Result { diff --git a/lib/vm/src/func_data_registry.rs b/lib/vm/src/func_data_registry.rs new file mode 100644 index 000000000..7fc9fc24d --- /dev/null +++ b/lib/vm/src/func_data_registry.rs @@ -0,0 +1,121 @@ +//! A registry for `VMFuncRef`s. This allows us to deduplicate funcrefs so that +//! identical `VMCallerCheckedAnyfunc`s will give us identical funcrefs. +//! +//! This registry also helps ensure that the `VMFuncRef`s can stay valid for as +//! long as we need them to. + +use crate::vmcontext::VMCallerCheckedAnyfunc; +use loupe::MemoryUsage; +use std::collections::HashMap; +use std::sync::Mutex; + +/// The registry that holds the values that `VMFuncRef`s point to. +#[derive(Debug, MemoryUsage)] +pub struct FuncDataRegistry { + // This structure is stored in an `Engine` and is intended to be shared + // across many instances. Ideally instances can themselves be sent across + // threads, and ideally we can compile across many threads. As a result we + // use interior mutability here with a lock to avoid having callers to + // externally synchronize calls to compilation. + inner: Mutex, +} + +// We use raw pointers but the data never moves, so it's not a problem +unsafe impl Send for FuncDataRegistry {} +unsafe impl Sync for FuncDataRegistry {} + +/// A function reference. A single word that points to metadata about a function. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, MemoryUsage)] +pub struct VMFuncRef(pub(crate) *const VMCallerCheckedAnyfunc); + +impl wasmer_types::NativeWasmType for VMFuncRef { + const WASM_TYPE: wasmer_types::Type = wasmer_types::Type::FuncRef; + type Abi = Self; + + #[inline] + fn from_abi(abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self) -> Self::Abi { + self + } + + #[inline] + fn to_binary(self) -> i128 { + self.0 as _ + } + + #[inline] + fn from_binary(bits: i128) -> Self { + // TODO: ensure that the safety invariants are actually upheld here + Self(bits as _) + } +} + +impl VMFuncRef { + /// Check if the FuncRef is null + // TODO: make this const when `std::ptr::is_null` is const + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + /// Create a new null FuncRef + pub const fn null() -> Self { + Self(std::ptr::null()) + } +} + +impl std::ops::Deref for VMFuncRef { + type Target = *const VMCallerCheckedAnyfunc; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for VMFuncRef { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +// We use raw pointers but the data never moves, so it's not a problem +// TODO: update docs +unsafe impl Send for VMFuncRef {} +unsafe impl Sync for VMFuncRef {} + +#[derive(Debug, Default, MemoryUsage)] +struct Inner { + func_data: Vec>, + anyfunc_to_index: HashMap, +} + +impl FuncDataRegistry { + /// Create a new `FuncDataRegistry`. + pub fn new() -> Self { + Self { + inner: Default::default(), + } + } + + /// Register a signature and return its unique index. + pub fn register(&self, anyfunc: VMCallerCheckedAnyfunc) -> VMFuncRef { + let mut inner = self.inner.lock().unwrap(); + if let Some(&idx) = inner.anyfunc_to_index.get(&anyfunc) { + let data: &Box<_> = &inner.func_data[idx]; + let inner_ptr: &VMCallerCheckedAnyfunc = &*data; + VMFuncRef(inner_ptr) + } else { + let idx = inner.func_data.len(); + inner.func_data.push(Box::new(anyfunc.clone())); + inner.anyfunc_to_index.insert(anyfunc, idx); + + let data: &Box<_> = &inner.func_data[idx]; + let inner_ptr: &VMCallerCheckedAnyfunc = &*data; + VMFuncRef(inner_ptr) + } + } +} diff --git a/lib/vm/src/global.rs b/lib/vm/src/global.rs index 35f41fdfd..9917c06be 100644 --- a/lib/vm/src/global.rs +++ b/lib/vm/src/global.rs @@ -4,7 +4,7 @@ use std::cell::UnsafeCell; use std::ptr::NonNull; use std::sync::Mutex; use thiserror::Error; -use wasmer_types::{GlobalType, Mutability, Type, Value}; +use wasmer_types::{GlobalType, Mutability, Type, Value, WasmValueType}; #[derive(Debug, MemoryUsage)] /// A Global instance @@ -65,7 +65,10 @@ impl Global { } /// Get a value from the global. - pub fn get(&self) -> Value { + // TODO(reftypes): the `&dyn Any` here for `Store` is a work-around for the fact + // that `Store` is defined in `API` when we need it earlier. Ideally this should + // be removed. + pub fn get(&self, store: &dyn std::any::Any) -> Value { let _global_guard = self.lock.lock().unwrap(); unsafe { let definition = &*self.vm_global_definition.get(); @@ -75,7 +78,16 @@ impl Global { Type::F32 => Value::F32(definition.to_f32()), Type::F64 => Value::F64(definition.to_f64()), Type::V128 => Value::V128(definition.to_u128()), - _ => unimplemented!("Global::get for {:?}", self.ty), + Type::ExternRef => Value::ExternRef(definition.to_externref().into()), + Type::FuncRef => { + let p = definition.to_u128() as i128; + if p as usize == 0 { + Value::FuncRef(None) + } else { + let v = T::read_value_from(store, &p); + Value::FuncRef(Some(v)) + } + } } } } @@ -84,7 +96,7 @@ impl Global { /// /// # Safety /// The caller should check that the `val` comes from the same store as this global. - pub unsafe fn set(&self, val: Value) -> Result<(), GlobalError> { + pub unsafe fn set(&self, val: Value) -> Result<(), GlobalError> { let _global_guard = self.lock.lock().unwrap(); if self.ty().mutability != Mutability::Var { return Err(GlobalError::ImmutableGlobalCannotBeSet); @@ -104,7 +116,7 @@ impl Global { /// The caller should check that the `val` comes from the same store as this global. /// The caller should also ensure that this global is synchronized. Otherwise, use /// `set` instead. - pub unsafe fn set_unchecked(&self, val: Value) -> Result<(), GlobalError> { + pub unsafe fn set_unchecked(&self, val: Value) -> Result<(), GlobalError> { // ideally we'd use atomics for the global value rather than needing to lock it let definition = &mut *self.vm_global_definition.get(); match val { @@ -113,7 +125,15 @@ impl Global { Value::F32(f) => *definition.as_f32_mut() = f, Value::F64(f) => *definition.as_f64_mut() = f, Value::V128(x) => *definition.as_bytes_mut() = x.to_ne_bytes(), - _ => unimplemented!("Global::set for {:?}", val.ty()), + Value::ExternRef(r) => { + let extern_ref = definition.as_externref_mut(); + extern_ref.ref_drop(); + *extern_ref = r.into() + } + Value::FuncRef(None) => *definition.as_u128_mut() = 0, + Value::FuncRef(Some(r)) => { + r.write_value_to(definition.as_u128_mut() as *mut u128 as *mut i128) + } } Ok(()) } diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index c0b4ae7a0..4108e63d3 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -14,10 +14,11 @@ pub use allocator::InstanceAllocator; pub use r#ref::InstanceRef; use crate::export::VMExport; +use crate::func_data_registry::{FuncDataRegistry, VMFuncRef}; use crate::global::Global; use crate::imports::Imports; use crate::memory::{Memory, MemoryError}; -use crate::table::Table; +use crate::table::{Table, TableElement}; use crate::trap::{catch_traps, init_traps, Trap, TrapCode}; use crate::vmcontext::{ VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, @@ -32,6 +33,7 @@ use memoffset::offset_of; use more_asserts::assert_lt; use std::any::Any; use std::cell::{Cell, RefCell}; +use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; use std::ffi; use std::fmt; @@ -84,11 +86,14 @@ pub(crate) struct Instance { /// Passive elements in this instantiation. As `elem.drop`s happen, these /// entries get removed. - passive_elements: RefCell>>>, + passive_elements: RefCell>>, /// Passive data segments from our module. As `data.drop`s happen, entries /// get removed. A missing entry is considered equivalent to an empty slice. - passive_data: RefCell>>>, + passive_data: RefCell>>, + + /// mapping of function indices to their func ref backing data. + funcrefs: BoxedSlice, /// Hosts can store arbitrary per-instance information here. host_state: Box, @@ -216,17 +221,11 @@ impl Instance { .cast() } - /// Return the indexed `VMSharedSignatureIndex`. - fn signature_id(&self, index: SignatureIndex) -> VMSharedSignatureIndex { - let index = usize::try_from(index.as_u32()).unwrap(); - unsafe { *self.signature_ids_ptr().add(index) } - } - fn module(&self) -> &Arc { &self.module } - fn module_ref(&self) -> &ModuleInfo { + pub(crate) fn module_ref(&self) -> &ModuleInfo { &*self.module } @@ -524,38 +523,90 @@ impl Instance { from.size() } + /// Returns the number of elements in a given table. + pub(crate) fn table_size(&self, table_index: LocalTableIndex) -> u32 { + self.tables + .get(table_index) + .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) + .size() + } + + /// Returns the number of elements in a given imported table. + /// + /// # Safety + /// `table_index` must be a valid, imported table index. + pub(crate) unsafe fn imported_table_size(&self, table_index: TableIndex) -> u32 { + let import = self.imported_table(table_index); + let from = import.from.as_ref(); + from.size() + } + /// Grow table by the specified amount of elements. /// /// Returns `None` if table can't be grown by the specified amount /// of elements. - pub(crate) fn table_grow(&self, table_index: LocalTableIndex, delta: u32) -> Option { + pub(crate) fn table_grow( + &self, + table_index: LocalTableIndex, + delta: u32, + init_value: TableElement, + ) -> Option { let result = self .tables .get(table_index) .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) - .grow(delta); + .grow(delta, init_value); result } + /// Grow table by the specified amount of elements. + /// + /// # Safety + /// `table_index` must be a valid, imported table index. + pub(crate) unsafe fn imported_table_grow( + &self, + table_index: TableIndex, + delta: u32, + init_value: TableElement, + ) -> Option { + let import = self.imported_table(table_index); + let from = import.from.as_ref(); + from.grow(delta.into(), init_value) + } + /// Get table element by index. - fn table_get( + pub(crate) fn table_get( &self, table_index: LocalTableIndex, index: u32, - ) -> Option { + ) -> Option { self.tables .get(table_index) .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) .get(index) } + /// Returns the element at the given index. + /// + /// # Safety + /// `table_index` must be a valid, imported table index. + pub(crate) unsafe fn imported_table_get( + &self, + table_index: TableIndex, + index: u32, + ) -> Option { + let import = self.imported_table(table_index); + let from = import.from.as_ref(); + from.get(index) + } + /// Set table element by index. - fn table_set( + pub(crate) fn table_set( &self, table_index: LocalTableIndex, index: u32, - val: VMCallerCheckedAnyfunc, + val: TableElement, ) -> Result<(), Trap> { self.tables .get(table_index) @@ -563,32 +614,31 @@ impl Instance { .set(index, val) } - /// Get a `VMCallerCheckedAnyfunc` for the given `FunctionIndex`. - fn get_caller_checked_anyfunc(&self, index: FunctionIndex) -> VMCallerCheckedAnyfunc { + /// Set table element by index for an imported table. + /// + /// # Safety + /// `table_index` must be a valid, imported table index. + pub(crate) unsafe fn imported_table_set( + &self, + table_index: TableIndex, + index: u32, + val: TableElement, + ) -> Result<(), Trap> { + let import = self.imported_table(table_index); + let from = import.from.as_ref(); + from.set(index, val) + } + + pub(crate) fn func_ref(&self, function_index: FunctionIndex) -> Option { + Some(self.get_vm_funcref(function_index)) + } + + /// Get a `VMFuncRef` for the given `FunctionIndex`. + fn get_vm_funcref(&self, index: FunctionIndex) -> VMFuncRef { if index == FunctionIndex::reserved_value() { - return VMCallerCheckedAnyfunc::default(); - } - - let sig = self.module.functions[index]; - let type_index = self.signature_id(sig); - - let (func_ptr, vmctx) = if let Some(def_index) = self.module.local_func_index(index) { - ( - self.functions[def_index].0 as *const _, - VMFunctionEnvironment { - vmctx: self.vmctx_ptr(), - }, - ) - } else { - let import = self.imported_function(index); - (import.body, import.environment) - }; - - VMCallerCheckedAnyfunc { - func_ptr, - type_index, - vmctx, + return VMFuncRef::null(); } + self.funcrefs[index] } /// The `table.init` operation: initializes a portion of a table with a @@ -611,9 +661,8 @@ impl Instance { let table = self.get_table(table_index); let passive_elements = self.passive_elements.borrow(); let elem = passive_elements - .get(elem_index) - .and_then(|e| e.as_ref().map(|e| &**e)) - .unwrap_or(&[]); + .get(&elem_index) + .map_or::<&[VMFuncRef], _>(&[], |e| &**e); if src .checked_add(len) @@ -625,7 +674,40 @@ impl Instance { for (dst, src) in (dst..dst + len).zip(src..src + len) { table - .set(dst, elem[src as usize].clone()) + .set(dst, TableElement::FuncRef(elem[src as usize])) + .expect("should never panic because we already did the bounds check above"); + } + + Ok(()) + } + + /// The `table.fill` operation: fills a portion of a table with a given value. + /// + /// # Errors + /// + /// Returns a `Trap` error when the range within the table is out of bounds + pub(crate) fn table_fill( + &self, + table_index: TableIndex, + start_index: u32, + item: TableElement, + len: u32, + ) -> Result<(), Trap> { + // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init + + let table = self.get_table(table_index); + let table_size = table.size() as usize; + + if start_index + .checked_add(len) + .map_or(true, |n| n as usize > table_size) + { + return Err(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds)); + } + + for i in start_index..(start_index + len) { + table + .set(i, item.clone()) .expect("should never panic because we already did the bounds check above"); } @@ -637,7 +719,7 @@ impl Instance { // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-elem-drop let mut passive_elements = self.passive_elements.borrow_mut(); - passive_elements[elem_index] = None; + passive_elements.remove(&elem_index); // Note that we don't check that we actually removed an element because // dropping a non-passive element is a no-op (not a trap). } @@ -730,10 +812,7 @@ impl Instance { let memory = self.get_memory(memory_index); let passive_data = self.passive_data.borrow(); - let data = passive_data - .get(data_index) - .and_then(|data| data.as_ref().map(|d| &**d)) - .unwrap_or(&[][..]); + let data = passive_data.get(&data_index).map_or(&[][..], |d| &**d); if src .checked_add(len) @@ -759,7 +838,7 @@ impl Instance { /// Drop the given data segment, truncating its length to zero. pub(crate) fn data_drop(&self, data_index: DataIndex) { let mut passive_data = self.passive_data.borrow_mut(); - passive_data[data_index] = None; + passive_data.remove(&data_index); } /// Get a table by index regardless of whether it is locally-defined or an @@ -828,6 +907,7 @@ impl InstanceHandle { finished_globals: BoxedSlice>, imports: Imports, vmshared_signatures: BoxedSlice, + func_data_registry: &FuncDataRegistry, host_state: Box, imported_function_envs: BoxedSlice, ) -> Result { @@ -836,16 +916,12 @@ impl InstanceHandle { .map(|m| m.vmglobal()) .collect::>() .into_boxed_slice(); - let passive_data = RefCell::new( - module - .passive_data - .values() - .map(|data| Some(data.clone())) - .collect(), - ); + let passive_data = RefCell::new(module.passive_data.clone()); let handle = { let offsets = allocator.offsets().clone(); + // use dummy value to create an instance so we can get the vmctx pointer + let funcrefs = PrimaryMap::new().into_boxed_slice(); // Create the `Instance`. The unique, the One. let instance = Instance { module, @@ -858,12 +934,27 @@ impl InstanceHandle { passive_elements: Default::default(), passive_data, host_state, + funcrefs, signal_handler: Cell::new(None), imported_function_envs, vmctx: VMContext {}, }; - let instance_ref = allocator.write_instance(instance); + let mut instance_ref = allocator.write_instance(instance); + + // Set the funcrefs after we've built the instance + { + let instance = instance_ref.as_mut(); + let vmctx_ptr = instance.vmctx_ptr(); + instance.funcrefs = build_funcrefs( + &*instance.module, + &imports, + &instance.functions, + func_data_registry, + &vmshared_signatures, + vmctx_ptr, + ); + } Self { instance: instance_ref, @@ -1109,18 +1200,21 @@ impl InstanceHandle { /// /// Returns `None` if memory can't be grown by the specified amount /// of pages. - pub fn table_grow(&self, table_index: LocalTableIndex, delta: u32) -> Option { - self.instance().as_ref().table_grow(table_index, delta) + pub fn table_grow( + &self, + table_index: LocalTableIndex, + delta: u32, + init_value: TableElement, + ) -> Option { + self.instance() + .as_ref() + .table_grow(table_index, delta, init_value) } /// Get table element reference. /// /// Returns `None` if index is out of bounds. - pub fn table_get( - &self, - table_index: LocalTableIndex, - index: u32, - ) -> Option { + pub fn table_get(&self, table_index: LocalTableIndex, index: u32) -> Option { self.instance().as_ref().table_get(table_index, index) } @@ -1131,7 +1225,7 @@ impl InstanceHandle { &self, table_index: LocalTableIndex, index: u32, - val: VMCallerCheckedAnyfunc, + val: TableElement, ) -> Result<(), Trap> { self.instance().as_ref().table_set(table_index, index, val) } @@ -1307,9 +1401,12 @@ fn initialize_tables(instance: &Instance) -> Result<(), Trap> { } for (i, func_idx) in init.elements.iter().enumerate() { - let anyfunc = instance.get_caller_checked_anyfunc(*func_idx); + let anyfunc = instance.get_vm_funcref(*func_idx); table - .set(u32::try_from(start + i).unwrap(), anyfunc) + .set( + u32::try_from(start + i).unwrap(), + TableElement::FuncRef(anyfunc), + ) .unwrap(); } } @@ -1327,20 +1424,22 @@ fn initialize_passive_elements(instance: &Instance) { "should only be called once, at initialization time" ); - for (segments, passive_element) in instance - .module - .passive_elements - .values() - .zip(passive_elements.values_mut()) - { - if segments.is_empty() { - continue; - } - *passive_element = segments + passive_elements.extend( + instance + .module + .passive_elements .iter() - .map(|s| Some(instance.get_caller_checked_anyfunc(*s))) - .collect(); - } + .filter(|(_, segments)| !segments.is_empty()) + .map(|(idx, segments)| { + ( + *idx, + segments + .iter() + .map(|s| instance.get_vm_funcref(*s)) + .collect(), + ) + }), + ); } /// Initialize the table memory from the provided initializers. @@ -1390,8 +1489,54 @@ fn initialize_globals(instance: &Instance) { }; *to = from; } - GlobalInit::RefNullConst | GlobalInit::RefFunc(_) => unimplemented!(), + GlobalInit::RefNullConst => *(*to).as_funcref_mut() = VMFuncRef::null(), + GlobalInit::RefFunc(func_idx) => { + let funcref = instance.func_ref(*func_idx).unwrap(); + *(*to).as_funcref_mut() = funcref; + } } } } } + +/// Eagerly builds all the `VMFuncRef`s for imported and local functions so that all +/// future funcref operations are just looking up this data. +fn build_funcrefs( + module_info: &ModuleInfo, + imports: &Imports, + finished_functions: &BoxedSlice, + func_data_registry: &FuncDataRegistry, + vmshared_signatures: &BoxedSlice, + vmctx_ptr: *mut VMContext, +) -> BoxedSlice { + let mut func_refs = PrimaryMap::with_capacity(module_info.functions.len()); + + // do imported functions + for (index, import) in imports.functions.iter() { + let sig_index = module_info.functions[index]; + let type_index = vmshared_signatures[sig_index]; + let anyfunc = VMCallerCheckedAnyfunc { + func_ptr: import.body, + type_index, + vmctx: import.environment, + }; + let func_ref = func_data_registry.register(anyfunc); + func_refs.push(func_ref); + } + + // do local functions + for (local_index, func_ptr) in finished_functions.iter() { + let index = module_info.func_index(local_index); + let sig_index = module_info.functions[index]; + let type_index = vmshared_signatures[sig_index]; + let anyfunc = VMCallerCheckedAnyfunc { + func_ptr: func_ptr.0, + type_index, + vmctx: VMFunctionEnvironment { vmctx: vmctx_ptr }, + }; + let func_ref = func_data_registry.register(anyfunc); + func_refs.push(func_ref); + } + + func_refs.into_boxed_slice() +} diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index 8d63c3039..a7f099385 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -21,6 +21,7 @@ )] mod export; +mod func_data_registry; mod global; mod imports; mod instance; @@ -37,6 +38,7 @@ mod vmoffsets; pub mod libcalls; pub use crate::export::*; +pub use crate::func_data_registry::{FuncDataRegistry, VMFuncRef}; pub use crate::global::*; pub use crate::imports::Imports; pub use crate::instance::{ @@ -47,7 +49,7 @@ pub use crate::mmap::Mmap; pub use crate::module::{ExportsIterator, ImportsIterator, ModuleInfo}; pub use crate::probestack::PROBESTACK; pub use crate::sig_registry::SignatureRegistry; -pub use crate::table::{LinearTable, Table, TableStyle}; +pub use crate::table::{LinearTable, Table, TableElement, TableStyle}; pub use crate::trap::*; pub use crate::vmcontext::{ VMBuiltinFunctionIndex, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, @@ -57,6 +59,7 @@ pub use crate::vmcontext::{ }; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMOffsets}; use loupe::MemoryUsage; +pub use wasmer_types::VMExternRef; /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/vm/src/libcalls.rs b/lib/vm/src/libcalls.rs index a602af1ac..f03fc2447 100644 --- a/lib/vm/src/libcalls.rs +++ b/lib/vm/src/libcalls.rs @@ -35,36 +35,42 @@ //! } //! ``` +use crate::func_data_registry::VMFuncRef; use crate::probestack::PROBESTACK; +use crate::table::{RawTableElement, TableElement}; use crate::trap::{raise_lib_trap, Trap, TrapCode}; use crate::vmcontext::VMContext; +use crate::VMExternRef; use loupe::MemoryUsage; use serde::{Deserialize, Serialize}; use std::fmt; -use wasmer_types::{DataIndex, ElemIndex, LocalMemoryIndex, MemoryIndex, TableIndex}; +use wasmer_types::{ + DataIndex, ElemIndex, FunctionIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, + TableIndex, Type, +}; /// Implementation of f32.ceil #[no_mangle] -pub extern "C" fn wasmer_f32_ceil(x: f32) -> f32 { +pub extern "C" fn wasmer_vm_f32_ceil(x: f32) -> f32 { x.ceil() } /// Implementation of f32.floor #[no_mangle] -pub extern "C" fn wasmer_f32_floor(x: f32) -> f32 { +pub extern "C" fn wasmer_vm_f32_floor(x: f32) -> f32 { x.floor() } /// Implementation of f32.trunc #[no_mangle] -pub extern "C" fn wasmer_f32_trunc(x: f32) -> f32 { +pub extern "C" fn wasmer_vm_f32_trunc(x: f32) -> f32 { x.trunc() } /// Implementation of f32.nearest #[allow(clippy::float_arithmetic, clippy::float_cmp)] #[no_mangle] -pub extern "C" fn wasmer_f32_nearest(x: f32) -> f32 { +pub extern "C" fn wasmer_vm_f32_nearest(x: f32) -> f32 { // Rust doesn't have a nearest function, so do it manually. if x == 0.0 { // Preserve the sign of zero. @@ -90,26 +96,26 @@ pub extern "C" fn wasmer_f32_nearest(x: f32) -> f32 { /// Implementation of f64.ceil #[no_mangle] -pub extern "C" fn wasmer_f64_ceil(x: f64) -> f64 { +pub extern "C" fn wasmer_vm_f64_ceil(x: f64) -> f64 { x.ceil() } /// Implementation of f64.floor #[no_mangle] -pub extern "C" fn wasmer_f64_floor(x: f64) -> f64 { +pub extern "C" fn wasmer_vm_f64_floor(x: f64) -> f64 { x.floor() } /// Implementation of f64.trunc #[no_mangle] -pub extern "C" fn wasmer_f64_trunc(x: f64) -> f64 { +pub extern "C" fn wasmer_vm_f64_trunc(x: f64) -> f64 { x.trunc() } /// Implementation of f64.nearest #[allow(clippy::float_arithmetic, clippy::float_cmp)] #[no_mangle] -pub extern "C" fn wasmer_f64_nearest(x: f64) -> f64 { +pub extern "C" fn wasmer_vm_f64_nearest(x: f64) -> f64 { // Rust doesn't have a nearest function, so do it manually. if x == 0.0 { // Preserve the sign of zero. @@ -137,8 +143,9 @@ pub extern "C" fn wasmer_f64_nearest(x: f64) -> f64 { /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_memory32_grow( +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_memory32_grow( vmctx: *mut VMContext, delta: u32, memory_index: u32, @@ -156,8 +163,9 @@ pub unsafe extern "C" fn wasmer_memory32_grow( /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_imported_memory32_grow( +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_imported_memory32_grow( vmctx: *mut VMContext, delta: u32, memory_index: u32, @@ -175,8 +183,9 @@ pub unsafe extern "C" fn wasmer_imported_memory32_grow( /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_memory32_size(vmctx: *mut VMContext, memory_index: u32) -> u32 { +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_memory32_size(vmctx: *mut VMContext, memory_index: u32) -> u32 { let instance = (&*vmctx).instance(); let memory_index = LocalMemoryIndex::from_u32(memory_index); @@ -187,8 +196,9 @@ pub unsafe extern "C" fn wasmer_memory32_size(vmctx: *mut VMContext, memory_inde /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_imported_memory32_size( +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_imported_memory32_size( vmctx: *mut VMContext, memory_index: u32, ) -> u32 { @@ -202,8 +212,9 @@ pub unsafe extern "C" fn wasmer_imported_memory32_size( /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_table_copy( +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_table_copy( vmctx: *mut VMContext, dst_table_index: u32, src_table_index: u32, @@ -228,8 +239,9 @@ pub unsafe extern "C" fn wasmer_table_copy( /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_table_init( +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_table_init( vmctx: *mut VMContext, table_index: u32, elem_index: u32, @@ -248,12 +260,268 @@ pub unsafe extern "C" fn wasmer_table_init( } } +/// Implementation of `table.fill`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_table_fill( + vmctx: *mut VMContext, + table_index: u32, + start_idx: u32, + item: RawTableElement, + len: u32, +) { + let result = { + let table_index = TableIndex::from_u32(table_index); + let instance = (&*vmctx).instance(); + let elem = match instance.get_table(table_index).ty().ty { + Type::ExternRef => TableElement::ExternRef(item.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(item.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; + + instance.table_fill(table_index, start_idx, elem, len) + }; + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `table.size`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_table_size(vmctx: *mut VMContext, table_index: u32) -> u32 { + let instance = (&*vmctx).instance(); + let table_index = LocalTableIndex::from_u32(table_index); + + instance.table_size(table_index) +} + +/// Implementation of `table.size` for imported tables. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_imported_table_size( + vmctx: *mut VMContext, + table_index: u32, +) -> u32 { + let instance = (&*vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + + instance.imported_table_size(table_index) +} + +/// Implementation of `table.get`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_table_get( + vmctx: *mut VMContext, + table_index: u32, + elem_index: u32, +) -> RawTableElement { + let instance = (&*vmctx).instance(); + let table_index = LocalTableIndex::from_u32(table_index); + + // TODO: type checking, maybe have specialized accessors + match instance.table_get(table_index, elem_index) { + Some(table_ref) => table_ref.into(), + None => raise_lib_trap(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds)), + } +} + +/// Implementation of `table.get` for imported tables. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_imported_table_get( + vmctx: *mut VMContext, + table_index: u32, + elem_index: u32, +) -> RawTableElement { + let instance = (&*vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + + // TODO: type checking, maybe have specialized accessors + match instance.imported_table_get(table_index, elem_index) { + Some(table_ref) => table_ref.into(), + None => raise_lib_trap(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds)), + } +} + +/// Implementation of `table.set`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +/// +/// It is the caller's responsibility to increment the ref count of any ref counted +/// type before passing it to this function. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_table_set( + vmctx: *mut VMContext, + table_index: u32, + elem_index: u32, + value: RawTableElement, +) { + let instance = (&*vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + let table_index = instance + .module_ref() + .local_table_index(table_index) + .unwrap(); + + let elem = match instance.get_local_table(table_index).ty().ty { + Type::ExternRef => TableElement::ExternRef(value.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(value.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; + + // TODO: type checking, maybe have specialized accessors + let result = instance.table_set(table_index, elem_index, elem); + + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `table.set` for imported tables. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_imported_table_set( + vmctx: *mut VMContext, + table_index: u32, + elem_index: u32, + value: RawTableElement, +) { + let instance = (&*vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + let elem = match instance.get_table(table_index).ty().ty { + Type::ExternRef => TableElement::ExternRef(value.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(value.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; + + let result = instance.imported_table_set(table_index, elem_index, elem); + + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `table.grow` for locally-defined tables. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_table_grow( + vmctx: *mut VMContext, + init_value: RawTableElement, + delta: u32, + table_index: u32, +) -> u32 { + let instance = (&*vmctx).instance(); + let table_index = LocalTableIndex::from_u32(table_index); + + let init_value = match instance.get_local_table(table_index).ty().ty { + Type::ExternRef => TableElement::ExternRef(init_value.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(init_value.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; + + instance + .table_grow(table_index, delta, init_value) + .unwrap_or(u32::max_value()) +} + +/// Implementation of `table.grow` for imported tables. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_imported_table_grow( + vmctx: *mut VMContext, + init_value: RawTableElement, + delta: u32, + table_index: u32, +) -> u32 { + let instance = (&*vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + let init_value = match instance.get_table(table_index).ty().ty { + Type::ExternRef => TableElement::ExternRef(init_value.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(init_value.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; + + instance + .imported_table_grow(table_index, delta, init_value) + .unwrap_or(u32::max_value()) +} + +/// Implementation of `func.ref`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_func_ref( + vmctx: *mut VMContext, + function_index: u32, +) -> VMFuncRef { + let instance = (&*vmctx).instance(); + let function_index = FunctionIndex::from_u32(function_index); + + instance.func_ref(function_index).unwrap() +} + +/// Implementation of externref increment +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +/// +/// This function must only be called at precise locations to prevent memory leaks. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_externref_inc(externref: VMExternRef) { + externref.ref_clone(); +} + +/// Implementation of externref decrement +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +/// +/// This function must only be called at precise locations, otherwise use-after-free +/// and other serious memory bugs may occur. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_externref_dec(mut externref: VMExternRef) { + externref.ref_drop() +} + /// Implementation of `elem.drop`. /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_elem_drop(vmctx: *mut VMContext, elem_index: u32) { +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_elem_drop(vmctx: *mut VMContext, elem_index: u32) { let elem_index = ElemIndex::from_u32(elem_index); let instance = (&*vmctx).instance(); instance.elem_drop(elem_index); @@ -263,8 +531,9 @@ pub unsafe extern "C" fn wasmer_elem_drop(vmctx: *mut VMContext, elem_index: u32 /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_local_memory_copy( +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_memory32_copy( vmctx: *mut VMContext, memory_index: u32, dst: u32, @@ -285,8 +554,9 @@ pub unsafe extern "C" fn wasmer_local_memory_copy( /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_imported_memory_copy( +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_imported_memory32_copy( vmctx: *mut VMContext, memory_index: u32, dst: u32, @@ -307,8 +577,9 @@ pub unsafe extern "C" fn wasmer_imported_memory_copy( /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_memory_fill( +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_memory32_fill( vmctx: *mut VMContext, memory_index: u32, dst: u32, @@ -329,8 +600,9 @@ pub unsafe extern "C" fn wasmer_memory_fill( /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_imported_memory_fill( +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_imported_memory32_fill( vmctx: *mut VMContext, memory_index: u32, dst: u32, @@ -351,8 +623,9 @@ pub unsafe extern "C" fn wasmer_imported_memory_fill( /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_memory_init( +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_memory32_init( vmctx: *mut VMContext, memory_index: u32, data_index: u32, @@ -375,8 +648,9 @@ pub unsafe extern "C" fn wasmer_memory_init( /// /// # Safety /// -/// `vmctx` must be valid and not null. -pub unsafe extern "C" fn wasmer_data_drop(vmctx: *mut VMContext, data_index: u32) { +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn wasmer_vm_data_drop(vmctx: *mut VMContext, data_index: u32) { let data_index = DataIndex::from_u32(data_index); let instance = (&*vmctx).instance(); instance.data_drop(data_index) @@ -389,7 +663,7 @@ pub unsafe extern "C" fn wasmer_data_drop(vmctx: *mut VMContext, data_index: u32 /// Only safe to call when wasm code is on the stack, aka `wasmer_call` or /// `wasmer_call_trampoline` must have been previously called. #[no_mangle] -pub unsafe extern "C" fn wasmer_raise_trap(trap_code: TrapCode) -> ! { +pub unsafe extern "C" fn wasmer_vm_raise_trap(trap_code: TrapCode) -> ! { let trap = Trap::new_from_runtime(trap_code); raise_lib_trap(trap) } @@ -401,7 +675,7 @@ pub unsafe extern "C" fn wasmer_raise_trap(trap_code: TrapCode) -> ! { /// This function does not follow the standard function ABI, and is called as /// part of the function prologue. #[no_mangle] -pub static wasmer_probestack: unsafe extern "C" fn() = PROBESTACK; +pub static wasmer_vm_probestack: unsafe extern "C" fn() = PROBESTACK; /// The name of a runtime library routine. /// @@ -426,55 +700,160 @@ pub enum LibCall { /// nearest.f64 NearestF64, - /// probe for stack overflow. These are emitted for functions which need - /// when the `enable_probestack` setting is true. - Probestack, + /// trunc.f32 + TruncF32, + + /// trunc.f64 + TruncF64, + + /// memory.size for local functions + Memory32Size, + + /// memory.size for imported functions + ImportedMemory32Size, + + /// table.copy + TableCopy, + + /// table.init + TableInit, + + /// table.fill + TableFill, + + /// table.size for local tables + TableSize, + + /// table.size for imported tables + ImportedTableSize, + + /// table.get for local tables + TableGet, + + /// table.get for imported tables + ImportedTableGet, + + /// table.set for local tables + TableSet, + + /// table.set for imported tables + ImportedTableSet, + + /// table.grow for local tables + TableGrow, + + /// table.grow for imported tables + ImportedTableGrow, + + /// ref.func + FuncRef, + + /// elem.drop + ElemDrop, + + /// memory.copy for local memories + Memory32Copy, + + /// memory.copy for imported memories + ImportedMemory32Copy, + + /// memory.fill for local memories + Memory32Fill, + + /// memory.fill for imported memories + ImportedMemory32Fill, + + /// memory.init + Memory32Init, + + /// data.drop + DataDrop, /// A custom trap RaiseTrap, - /// trunc.f32 - TruncF32, - - /// frunc.f64 - TruncF64, + /// probe for stack overflow. These are emitted for functions which need + /// when the `enable_probestack` setting is true. + Probestack, } impl LibCall { /// The function pointer to a libcall pub fn function_pointer(self) -> usize { match self { - Self::CeilF32 => wasmer_f32_ceil as usize, - Self::CeilF64 => wasmer_f64_ceil as usize, - Self::FloorF32 => wasmer_f32_floor as usize, - Self::FloorF64 => wasmer_f64_floor as usize, - Self::NearestF32 => wasmer_f32_nearest as usize, - Self::NearestF64 => wasmer_f64_nearest as usize, - Self::Probestack => wasmer_probestack as usize, - Self::RaiseTrap => wasmer_raise_trap as usize, - Self::TruncF32 => wasmer_f32_trunc as usize, - Self::TruncF64 => wasmer_f64_trunc as usize, + Self::CeilF32 => wasmer_vm_f32_ceil as usize, + Self::CeilF64 => wasmer_vm_f64_ceil as usize, + Self::FloorF32 => wasmer_vm_f32_floor as usize, + Self::FloorF64 => wasmer_vm_f64_floor as usize, + Self::NearestF32 => wasmer_vm_f32_nearest as usize, + Self::NearestF64 => wasmer_vm_f64_nearest as usize, + Self::TruncF32 => wasmer_vm_f32_trunc as usize, + Self::TruncF64 => wasmer_vm_f64_trunc as usize, + Self::Memory32Size => wasmer_vm_memory32_size as usize, + Self::ImportedMemory32Size => wasmer_vm_imported_memory32_size as usize, + Self::TableCopy => wasmer_vm_table_copy as usize, + Self::TableInit => wasmer_vm_table_init as usize, + Self::TableFill => wasmer_vm_table_fill as usize, + Self::TableSize => wasmer_vm_table_size as usize, + Self::ImportedTableSize => wasmer_vm_imported_table_size as usize, + Self::TableGet => wasmer_vm_table_get as usize, + Self::ImportedTableGet => wasmer_vm_imported_table_get as usize, + Self::TableSet => wasmer_vm_table_set as usize, + Self::ImportedTableSet => wasmer_vm_imported_table_set as usize, + Self::TableGrow => wasmer_vm_table_grow as usize, + Self::ImportedTableGrow => wasmer_vm_imported_table_grow as usize, + Self::FuncRef => wasmer_vm_func_ref as usize, + Self::ElemDrop => wasmer_vm_elem_drop as usize, + Self::Memory32Copy => wasmer_vm_memory32_copy as usize, + Self::ImportedMemory32Copy => wasmer_vm_imported_memory32_copy as usize, + Self::Memory32Fill => wasmer_vm_memory32_fill as usize, + Self::ImportedMemory32Fill => wasmer_vm_memory32_fill as usize, + Self::Memory32Init => wasmer_vm_memory32_init as usize, + Self::DataDrop => wasmer_vm_data_drop as usize, + Self::Probestack => wasmer_vm_probestack as usize, + Self::RaiseTrap => wasmer_vm_raise_trap as usize, } } /// Return the function name associated to the libcall. pub fn to_function_name(&self) -> &str { match self { - Self::CeilF32 => "wasmer_f32_ceil", - Self::CeilF64 => "wasmer_f64_ceil", - Self::FloorF32 => "wasmer_f32_floor", - Self::FloorF64 => "wasmer_f64_floor", - Self::NearestF32 => "wasmer_f32_nearest", - Self::NearestF64 => "wasmer_f64_nearest", + Self::CeilF32 => "wasmer_vm_f32_ceil", + Self::CeilF64 => "wasmer_vm_f64_ceil", + Self::FloorF32 => "wasmer_vm_f32_floor", + Self::FloorF64 => "wasmer_vm_f64_floor", + Self::NearestF32 => "wasmer_vm_f32_nearest", + Self::NearestF64 => "wasmer_vm_f64_nearest", + Self::TruncF32 => "wasmer_vm_f32_trunc", + Self::TruncF64 => "wasmer_vm_f64_trunc", + Self::Memory32Size => "wasmer_vm_memory32_size", + Self::ImportedMemory32Size => "wasmer_vm_imported_memory32_size", + Self::TableCopy => "wasmer_vm_table_copy", + Self::TableInit => "wasmer_vm_table_init", + Self::TableFill => "wasmer_vm_table_fill", + Self::TableSize => "wasmer_vm_table_size", + Self::ImportedTableSize => "wasmer_vm_imported_table_size", + Self::TableGet => "wasmer_vm_table_get", + Self::ImportedTableGet => "wasmer_vm_imported_table_get", + Self::TableSet => "wasmer_vm_table_set", + Self::ImportedTableSet => "wasmer_vm_imported_table_set", + Self::TableGrow => "wasmer_vm_table_grow", + Self::ImportedTableGrow => "wasmer_vm_imported_table_grow", + Self::FuncRef => "wasmer_vm_func_ref", + Self::ElemDrop => "wasmer_vm_elem_drop", + Self::Memory32Copy => "wasmer_vm_memory32_copy", + Self::ImportedMemory32Copy => "wasmer_vm_imported_memory32_copy", + Self::Memory32Fill => "wasmer_vm_memory32_fill", + Self::ImportedMemory32Fill => "wasmer_vm_imported_memory32_fill", + Self::Memory32Init => "wasmer_vm_memory32_init", + Self::DataDrop => "wasmer_vm_data_drop", + Self::RaiseTrap => "wasmer_vm_raise_trap", // We have to do this because macOS requires a leading `_` and it's not // a normal function, it's a static variable, so we have to do it manually. #[cfg(target_os = "macos")] - Self::Probestack => "_wasmer_probestack", + Self::Probestack => "_wasmer_vm_probestack", #[cfg(not(target_os = "macos"))] - Self::Probestack => "wasmer_probestack", - Self::RaiseTrap => "wasmer_raise_trap", - Self::TruncF32 => "wasmer_f32_trunc", - Self::TruncF64 => "wasmer_f64_trunc", + Self::Probestack => "wasmer_vm_probestack", } } } diff --git a/lib/vm/src/module.rs b/lib/vm/src/module.rs index e02f068a4..9817bc291 100644 --- a/lib/vm/src/module.rs +++ b/lib/vm/src/module.rs @@ -80,10 +80,10 @@ pub struct ModuleInfo { pub table_initializers: Vec, /// WebAssembly passive elements. - pub passive_elements: PrimaryMap>, + pub passive_elements: HashMap>, /// WebAssembly passive data segments. - pub passive_data: PrimaryMap>, + pub passive_data: HashMap>, /// WebAssembly global initializers. pub global_initializers: PrimaryMap, @@ -134,8 +134,8 @@ pub struct ArchivableModuleInfo { exports: ArchivableIndexMap, start_function: Option, table_initializers: Vec, - passive_elements: PrimaryMap>, - passive_data: PrimaryMap>, + passive_elements: HashMap>, + passive_data: HashMap>, global_initializers: PrimaryMap, function_names: HashMap, signatures: PrimaryMap, @@ -281,8 +281,8 @@ impl ModuleInfo { exports: IndexMap::new(), start_function: None, table_initializers: Vec::new(), - passive_elements: PrimaryMap::new(), - passive_data: PrimaryMap::new(), + passive_elements: HashMap::new(), + passive_data: HashMap::new(), global_initializers: PrimaryMap::new(), function_names: HashMap::new(), signatures: PrimaryMap::new(), @@ -301,7 +301,7 @@ impl ModuleInfo { /// Get the given passive element, if it exists. pub fn get_passive_element(&self, index: ElemIndex) -> Option<&[FunctionIndex]> { - self.passive_elements.get(index).map(|es| &**es) + self.passive_elements.get(&index).map(|es| &**es) } /// Get the exported signatures of the module diff --git a/lib/vm/src/table.rs b/lib/vm/src/table.rs index 10c9ea3ed..fcf9fcec5 100644 --- a/lib/vm/src/table.rs +++ b/lib/vm/src/table.rs @@ -5,11 +5,13 @@ //! //! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories. +use crate::func_data_registry::VMFuncRef; use crate::trap::{Trap, TrapCode}; -use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition}; -use loupe::MemoryUsage; #[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; +use crate::vmcontext::VMTableDefinition; +use crate::VMExternRef; +use loupe::{MemoryUsage, MemoryUsageTracker}; use serde::{Deserialize, Serialize}; use std::borrow::{Borrow, BorrowMut}; use std::cell::UnsafeCell; @@ -17,7 +19,7 @@ use std::convert::TryFrom; use std::fmt; use std::ptr::NonNull; use std::sync::Mutex; -use wasmer_types::{TableType, Type as ValType}; +use wasmer_types::{ExternRef, TableType, Type as ValType}; /// Implementation styles for WebAssembly tables. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, MemoryUsage)] @@ -45,19 +47,19 @@ pub trait Table: fmt::Debug + Send + Sync + MemoryUsage { /// /// Returns `None` if table can't be grown by the specified amount /// of elements, otherwise returns the previous size of the table. - fn grow(&self, delta: u32) -> Option; + fn grow(&self, delta: u32, init_value: TableElement) -> Option; /// Get reference to the specified element. /// /// Returns `None` if the index is out of bounds. - fn get(&self, index: u32) -> Option; + fn get(&self, index: u32) -> Option; /// Set reference to the specified element. /// /// # Errors /// /// Returns an error if the index is out of bounds. - fn set(&self, index: u32, func: VMCallerCheckedAnyfunc) -> Result<(), Trap>; + fn set(&self, index: u32, reference: TableElement) -> Result<(), Trap>; /// Return a `VMTableDefinition` for exposing the table to compiled wasm code. fn vmtable(&self) -> NonNull; @@ -109,11 +111,74 @@ pub trait Table: fmt::Debug + Send + Sync + MemoryUsage { } } +/// A reference stored in a table. Can be either an externref or a funcref. +#[derive(Debug, Clone)] +pub enum TableElement { + /// Opaque pointer to arbitrary host data. + // Note: we use `ExternRef` instead of `VMExternRef` here to ensure that we don't + // leak by not dec-refing on failure types. + ExternRef(ExternRef), + /// Pointer to function: contains enough information to call it. + FuncRef(VMFuncRef), +} + +impl From for RawTableElement { + fn from(other: TableElement) -> Self { + match other { + TableElement::ExternRef(extern_ref) => Self { + extern_ref: extern_ref.into(), + }, + TableElement::FuncRef(func_ref) => Self { func_ref }, + } + } +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union RawTableElement { + pub(crate) extern_ref: VMExternRef, + pub(crate) func_ref: VMFuncRef, +} + +#[cfg(test)] +#[test] +fn table_element_size_test() { + use std::mem::size_of; + assert_eq!(size_of::(), size_of::()); + assert_eq!(size_of::(), size_of::()); +} + +impl MemoryUsage for RawTableElement { + fn size_of_val(&self, _: &mut dyn MemoryUsageTracker) -> usize { + std::mem::size_of_val(self) + } +} + +impl fmt::Debug for RawTableElement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RawTableElement").finish() + } +} + +impl Default for RawTableElement { + fn default() -> Self { + Self { + func_ref: VMFuncRef::null(), + } + } +} + +impl Default for TableElement { + fn default() -> Self { + Self::FuncRef(VMFuncRef::null()) + } +} + /// A table instance. #[derive(Debug, MemoryUsage)] pub struct LinearTable { // TODO: we can remove the mutex by using atomic swaps and preallocating the max table size - vec: Mutex>, + vec: Mutex>, maximum: Option, /// The WebAssembly table description. table: TableType, @@ -171,8 +236,13 @@ impl LinearTable { vm_table_location: Option>, ) -> Result { match table.ty { - ValType::FuncRef => (), - ty => return Err(format!("tables of types other than anyfunc ({})", ty)), + ValType::FuncRef | ValType::ExternRef => (), + ty => { + return Err(format!( + "tables of types other than funcref or externref ({})", + ty + )) + } }; if let Some(max) = table.maximum { if max < table.minimum { @@ -184,7 +254,7 @@ impl LinearTable { } let table_minimum = usize::try_from(table.minimum) .map_err(|_| "Table minimum is bigger than usize".to_string())?; - let mut vec = vec![VMCallerCheckedAnyfunc::default(); table_minimum]; + let mut vec = vec![RawTableElement::default(); table_minimum]; let base = vec.as_mut_ptr(); match style { TableStyle::CallerChecksSignature => Ok(Self { @@ -252,7 +322,7 @@ impl Table for LinearTable { /// /// Returns `None` if table can't be grown by the specified amount /// of elements, otherwise returns the previous size of the table. - fn grow(&self, delta: u32) -> Option { + fn grow(&self, delta: u32, init_value: TableElement) -> Option { let mut vec_guard = self.vec.lock().unwrap(); let vec = vec_guard.borrow_mut(); let size = self.size(); @@ -260,10 +330,26 @@ impl Table for LinearTable { if self.maximum.map_or(false, |max| new_len > max) { return None; } - vec.resize( - usize::try_from(new_len).unwrap(), - VMCallerCheckedAnyfunc::default(), - ); + if new_len == size { + debug_assert_eq!(delta, 0); + return Some(size); + } + + // Update the ref count + let element = match init_value { + TableElement::ExternRef(extern_ref) => { + let extern_ref: VMExternRef = extern_ref.into(); + // We reduce the amount we increment by because `into` prevents + // dropping `init_value` (which is a caller-inc'd ref). + (new_len as usize) + .checked_sub(size as usize + 1) + .map(|val| extern_ref.ref_inc_by(val)); + RawTableElement { extern_ref } + } + TableElement::FuncRef(func_ref) => RawTableElement { func_ref }, + }; + + vec.resize(usize::try_from(new_len).unwrap(), element); // update table definition unsafe { @@ -278,9 +364,16 @@ impl Table for LinearTable { /// Get reference to the specified element. /// /// Returns `None` if the index is out of bounds. - fn get(&self, index: u32) -> Option { + fn get(&self, index: u32) -> Option { let vec_guard = self.vec.lock().unwrap(); - vec_guard.borrow().get(index as usize).cloned() + let raw_data = vec_guard.borrow().get(index as usize).cloned()?; + Some(match self.table.ty { + ValType::ExternRef => { + TableElement::ExternRef(unsafe { raw_data.extern_ref.ref_clone() }.into()) + } + ValType::FuncRef => TableElement::FuncRef(unsafe { raw_data.func_ref }), + _ => todo!("getting invalid type from table, handle this error"), + }) } /// Set reference to the specified element. @@ -288,12 +381,32 @@ impl Table for LinearTable { /// # Errors /// /// Returns an error if the index is out of bounds. - fn set(&self, index: u32, func: VMCallerCheckedAnyfunc) -> Result<(), Trap> { + fn set(&self, index: u32, reference: TableElement) -> Result<(), Trap> { let mut vec_guard = self.vec.lock().unwrap(); let vec = vec_guard.borrow_mut(); match vec.get_mut(index as usize) { Some(slot) => { - *slot = func; + match (self.table.ty, reference) { + (ValType::ExternRef, TableElement::ExternRef(extern_ref)) => { + let extern_ref = extern_ref.into(); + unsafe { + let elem = &mut *slot; + elem.extern_ref.ref_drop(); + elem.extern_ref = extern_ref + } + } + (ValType::FuncRef, r @ TableElement::FuncRef(_)) => { + let element_data = r.into(); + *slot = element_data; + } + // This path should never be hit by the generated code due to Wasm + // validation. + (ty, v) => panic!( + "Attempted to set a table of type {} with the value {:?}", + ty, v + ), + }; + Ok(()) } None => Err(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds)), diff --git a/lib/vm/src/trap/trapcode.rs b/lib/vm/src/trap/trapcode.rs index 31a25765f..ee3e910fb 100644 --- a/lib/vm/src/trap/trapcode.rs +++ b/lib/vm/src/trap/trapcode.rs @@ -86,7 +86,9 @@ impl TrapCode { Self::HeapSetterOutOfBounds => "memory out of bounds: data segment does not fit", Self::HeapAccessOutOfBounds => "out of bounds memory access", Self::HeapMisaligned => "misaligned heap", - Self::TableSetterOutOfBounds => "table out of bounds: elements segment does not fit", + Self::TableSetterOutOfBounds => { + "out of bounds table access: elements segment does not fit" + } Self::TableAccessOutOfBounds => "undefined element: out of bounds table access", Self::OutOfBounds => "out of bounds", Self::IndirectCallToNull => "uninitialized element", diff --git a/lib/vm/src/vmcontext.rs b/lib/vm/src/vmcontext.rs index 65657e23f..9d294e9fc 100644 --- a/lib/vm/src/vmcontext.rs +++ b/lib/vm/src/vmcontext.rs @@ -4,11 +4,13 @@ //! This file declares `VMContext` and several related structs which contain //! fields that compiled wasm code accesses directly. +use crate::func_data_registry::VMFuncRef; use crate::global::Global; use crate::instance::Instance; use crate::memory::Memory; use crate::table::Table; use crate::trap::{Trap, TrapCode}; +use crate::VMExternRef; use loupe::{MemoryUsage, MemoryUsageTracker, POINTER_BYTE_SIZE}; use std::any::Any; use std::convert::TryFrom; @@ -22,7 +24,7 @@ use std::u32; /// /// It may either be a pointer to the [`VMContext`] if it's a Wasm function /// or a pointer to arbitrary data controlled by the host if it's a host function. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Eq)] pub union VMFunctionEnvironment { /// Wasm functions take a pointer to [`VMContext`]. pub vmctx: *mut VMContext, @@ -51,6 +53,14 @@ impl std::cmp::PartialEq for VMFunctionEnvironment { } } +impl std::hash::Hash for VMFunctionEnvironment { + fn hash(&self, state: &mut H) { + unsafe { + self.vmctx.hash(state); + } + } +} + impl MemoryUsage for VMFunctionEnvironment { fn size_of_val(&self, _: &mut dyn MemoryUsageTracker) -> usize { mem::size_of_val(self) @@ -516,6 +526,8 @@ pub union VMGlobalDefinitionStorage { as_u64: u64, as_f64: f64, as_u128: u128, + as_funcref: VMFuncRef, + as_externref: VMExternRef, bytes: [u8; 16], } @@ -547,7 +559,7 @@ pub struct VMGlobalDefinition { #[cfg(test)] mod test_vmglobal_definition { use super::VMGlobalDefinition; - use crate::{ModuleInfo, VMOffsets}; + use crate::{ModuleInfo, VMFuncRef, VMOffsets}; use more_asserts::assert_ge; use std::mem::{align_of, size_of}; @@ -557,6 +569,7 @@ mod test_vmglobal_definition { assert_ge!(align_of::(), align_of::()); assert_ge!(align_of::(), align_of::()); assert_ge!(align_of::(), align_of::()); + assert_ge!(align_of::(), align_of::()); assert_ge!(align_of::(), align_of::<[u8; 16]>()); } @@ -700,6 +713,44 @@ impl VMGlobalDefinition { &mut self.storage.as_f64 } + /// Return a reference to the value as a `VMFuncRef`. + /// + /// If this is not a `VMFuncRef` typed global it is unspecified what value is returned. + pub fn to_funcref(&self) -> VMFuncRef { + unsafe { self.storage.as_funcref } + } + + /// Return a mutable reference to the value as a `VMFuncRef`. + /// + /// # Safety + /// + /// It is the callers responsibility to make sure the global has `VMFuncRef` type. + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_funcref_mut(&mut self) -> &mut VMFuncRef { + &mut self.storage.as_funcref + } + + /// Return a mutable reference to the value as an `VMExternRef`. + /// + /// # Safety + /// + /// It is the callers responsibility to make sure the global has I32 type. + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_externref_mut(&mut self) -> &mut VMExternRef { + &mut self.storage.as_externref + } + + /// Return a reference to the value as an `VMExternRef`. + /// + /// If this is not an I64 typed global it is unspecified what value is returned. + pub fn to_externref(&self) -> VMExternRef { + unsafe { self.storage.as_externref } + } + /// Return a reference to the value as an u128. /// /// If this is not an V128 typed global it is unspecified what value is returned. @@ -784,7 +835,7 @@ impl Default for VMSharedSignatureIndex { /// The VM caller-checked "anyfunc" record, for caller-side signature checking. /// It consists of the actual function pointer and a signature id to be checked /// by the caller. -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, MemoryUsage)] #[repr(C)] pub struct VMCallerCheckedAnyfunc { /// Function body. @@ -873,7 +924,7 @@ impl VMBuiltinFunctionIndex { Self(6) } /// Returns an index for wasm's `memory.copy` for locally defined memories. - pub const fn get_local_memory_copy_index() -> Self { + pub const fn get_memory_copy_index() -> Self { Self(7) } /// Returns an index for wasm's `memory.copy` for imported memories. @@ -900,9 +951,57 @@ impl VMBuiltinFunctionIndex { pub const fn get_raise_trap_index() -> Self { Self(13) } + /// Returns an index for wasm's `table.size` instruction for local tables. + pub const fn get_table_size_index() -> Self { + Self(14) + } + /// Returns an index for wasm's `table.size` instruction for imported tables. + pub const fn get_imported_table_size_index() -> Self { + Self(15) + } + /// Returns an index for wasm's `table.grow` instruction for local tables. + pub const fn get_table_grow_index() -> Self { + Self(16) + } + /// Returns an index for wasm's `table.grow` instruction for imported tables. + pub const fn get_imported_table_grow_index() -> Self { + Self(17) + } + /// Returns an index for wasm's `table.get` instruction for local tables. + pub const fn get_table_get_index() -> Self { + Self(18) + } + /// Returns an index for wasm's `table.get` instruction for imported tables. + pub const fn get_imported_table_get_index() -> Self { + Self(19) + } + /// Returns an index for wasm's `table.set` instruction for local tables. + pub const fn get_table_set_index() -> Self { + Self(20) + } + /// Returns an index for wasm's `table.set` instruction for imported tables. + pub const fn get_imported_table_set_index() -> Self { + Self(21) + } + /// Returns an index for wasm's `func.ref` instruction. + pub const fn get_func_ref_index() -> Self { + Self(22) + } + /// Returns an index for wasm's `table.fill` instruction for local tables. + pub const fn get_table_fill_index() -> Self { + Self(23) + } + /// Returns an index for a function to increment the externref count. + pub const fn get_externref_inc_index() -> Self { + Self(24) + } + /// Returns an index for a function to decrement the externref count. + pub const fn get_externref_dec_index() -> Self { + Self(25) + } /// Returns the total number of builtin functions. pub const fn builtin_functions_total_number() -> u32 { - 14 + 26 } /// Return the index as an u32 number. @@ -929,37 +1028,61 @@ impl VMBuiltinFunctionsArray { let mut ptrs = [0; Self::len()]; ptrs[VMBuiltinFunctionIndex::get_memory32_grow_index().index() as usize] = - wasmer_memory32_grow as usize; + wasmer_vm_memory32_grow as usize; ptrs[VMBuiltinFunctionIndex::get_imported_memory32_grow_index().index() as usize] = - wasmer_imported_memory32_grow as usize; + wasmer_vm_imported_memory32_grow as usize; ptrs[VMBuiltinFunctionIndex::get_memory32_size_index().index() as usize] = - wasmer_memory32_size as usize; + wasmer_vm_memory32_size as usize; ptrs[VMBuiltinFunctionIndex::get_imported_memory32_size_index().index() as usize] = - wasmer_imported_memory32_size as usize; + wasmer_vm_imported_memory32_size as usize; ptrs[VMBuiltinFunctionIndex::get_table_copy_index().index() as usize] = - wasmer_table_copy as usize; + wasmer_vm_table_copy as usize; ptrs[VMBuiltinFunctionIndex::get_table_init_index().index() as usize] = - wasmer_table_init as usize; + wasmer_vm_table_init as usize; ptrs[VMBuiltinFunctionIndex::get_elem_drop_index().index() as usize] = - wasmer_elem_drop as usize; + wasmer_vm_elem_drop as usize; - ptrs[VMBuiltinFunctionIndex::get_local_memory_copy_index().index() as usize] = - wasmer_local_memory_copy as usize; + ptrs[VMBuiltinFunctionIndex::get_memory_copy_index().index() as usize] = + wasmer_vm_memory32_copy as usize; ptrs[VMBuiltinFunctionIndex::get_imported_memory_copy_index().index() as usize] = - wasmer_imported_memory_copy as usize; + wasmer_vm_imported_memory32_copy as usize; ptrs[VMBuiltinFunctionIndex::get_memory_fill_index().index() as usize] = - wasmer_memory_fill as usize; + wasmer_vm_memory32_fill as usize; ptrs[VMBuiltinFunctionIndex::get_imported_memory_fill_index().index() as usize] = - wasmer_imported_memory_fill as usize; + wasmer_vm_imported_memory32_fill as usize; ptrs[VMBuiltinFunctionIndex::get_memory_init_index().index() as usize] = - wasmer_memory_init as usize; + wasmer_vm_memory32_init as usize; ptrs[VMBuiltinFunctionIndex::get_data_drop_index().index() as usize] = - wasmer_data_drop as usize; + wasmer_vm_data_drop as usize; ptrs[VMBuiltinFunctionIndex::get_raise_trap_index().index() as usize] = - wasmer_raise_trap as usize; + wasmer_vm_raise_trap as usize; + ptrs[VMBuiltinFunctionIndex::get_table_size_index().index() as usize] = + wasmer_vm_table_size as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_table_size_index().index() as usize] = + wasmer_vm_imported_table_size as usize; + ptrs[VMBuiltinFunctionIndex::get_table_grow_index().index() as usize] = + wasmer_vm_table_grow as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_table_grow_index().index() as usize] = + wasmer_vm_imported_table_grow as usize; + ptrs[VMBuiltinFunctionIndex::get_table_get_index().index() as usize] = + wasmer_vm_table_get as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_table_get_index().index() as usize] = + wasmer_vm_imported_table_get as usize; + ptrs[VMBuiltinFunctionIndex::get_table_set_index().index() as usize] = + wasmer_vm_table_set as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_table_set_index().index() as usize] = + wasmer_vm_imported_table_set as usize; + ptrs[VMBuiltinFunctionIndex::get_func_ref_index().index() as usize] = + wasmer_vm_func_ref as usize; + ptrs[VMBuiltinFunctionIndex::get_table_fill_index().index() as usize] = + wasmer_vm_table_fill as usize; + ptrs[VMBuiltinFunctionIndex::get_externref_inc_index().index() as usize] = + wasmer_vm_externref_inc as usize; + ptrs[VMBuiltinFunctionIndex::get_externref_dec_index().index() as usize] = + wasmer_vm_externref_dec as usize; debug_assert!(ptrs.iter().cloned().all(|p| p != 0)); diff --git a/lib/vm/src/vmoffsets.rs b/lib/vm/src/vmoffsets.rs index 6a6fa5b26..7d849e88e 100644 --- a/lib/vm/src/vmoffsets.rs +++ b/lib/vm/src/vmoffsets.rs @@ -335,6 +335,24 @@ impl VMOffsets { } } +/// Offsets for [`VMFuncRef`]. +/// +/// [`VMFuncRef`]: crate::func_data_registry::VMFuncRef +impl VMOffsets { + /// The offset to the pointer to the anyfunc inside the ref. + #[allow(clippy::erasing_op)] + pub const fn vm_funcref_anyfunc_ptr(&self) -> u8 { + 0 * self.pointer_size + } + + /// Return the size of [`VMFuncRef`]. + /// + /// [`VMFuncRef`]: crate::func_data_registry::VMFuncRef + pub const fn size_of_vm_funcref(&self) -> u8 { + 1 * self.pointer_size + } +} + /// Offsets for [`VMContext`]. /// /// [`VMContext`]: crate::vmcontext::VMContext diff --git a/tests/ignores.txt b/tests/ignores.txt index c1b9bd39c..02403d67f 100644 --- a/tests/ignores.txt +++ b/tests/ignores.txt @@ -34,7 +34,6 @@ cranelift::spec::simd::simd_i8x16_sat_arith on aarch64 cranelift::spec::simd::simd_lane on aarch64 cranelift::spec::skip_stack_guard_page on aarch64 - # SIMD changes # due to breaking changes in the SIMD proposal, we have to disable these spec tests # note we've not pulled in the updated spec tests yet, so expect more breakage diff --git a/tests/lib/engine-dummy/src/artifact.rs b/tests/lib/engine-dummy/src/artifact.rs index 4467d2205..a5a993c40 100644 --- a/tests/lib/engine-dummy/src/artifact.rs +++ b/tests/lib/engine-dummy/src/artifact.rs @@ -16,8 +16,8 @@ use wasmer_types::{ TableIndex, }; use wasmer_vm::{ - FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMContext, VMFunctionBody, - VMSharedSignatureIndex, VMTrampoline, + FuncDataRegistry, FunctionBodyPtr, MemoryStyle, ModuleInfo, TableStyle, VMContext, + VMFunctionBody, VMSharedSignatureIndex, VMTrampoline, }; /// Serializable struct for the artifact @@ -44,6 +44,7 @@ pub struct DummyArtifact { finished_function_call_trampolines: BoxedSlice, finished_dynamic_function_trampolines: BoxedSlice, signatures: BoxedSlice, + func_data_registry: Arc, } extern "C" fn dummy_function(_context: *mut VMContext) { @@ -184,6 +185,7 @@ impl DummyArtifact { finished_function_call_trampolines, finished_dynamic_function_trampolines, signatures, + func_data_registry: engine.func_data().clone(), }) } } @@ -237,6 +239,10 @@ impl Artifact for DummyArtifact { &self.signatures } + fn func_data_registry(&self) -> &FuncDataRegistry { + &self.func_data_registry + } + #[cfg(feature = "serialize")] fn serialize(&self) -> Result, SerializeError> { let bytes = bincode::serialize(&self.metadata) diff --git a/tests/lib/engine-dummy/src/engine.rs b/tests/lib/engine-dummy/src/engine.rs index ca9b0c4a9..cdc80b81b 100644 --- a/tests/lib/engine-dummy/src/engine.rs +++ b/tests/lib/engine-dummy/src/engine.rs @@ -6,7 +6,10 @@ use std::sync::Arc; use wasmer_compiler::{CompileError, Features, Target}; use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables}; use wasmer_types::FunctionType; -use wasmer_vm::{SignatureRegistry, VMContext, VMFunctionBody, VMSharedSignatureIndex}; +use wasmer_vm::{ + FuncDataRegistry, SignatureRegistry, VMCallerCheckedAnyfunc, VMContext, VMFuncRef, + VMFunctionBody, VMSharedSignatureIndex, +}; #[allow(dead_code)] extern "C" fn dummy_trampoline( @@ -21,6 +24,7 @@ extern "C" fn dummy_trampoline( #[derive(Clone, MemoryUsage)] pub struct DummyEngine { signatures: Arc, + func_data: Arc, features: Arc, target: Arc, engine_id: EngineId, @@ -31,6 +35,7 @@ impl DummyEngine { pub fn new() -> Self { Self { signatures: Arc::new(SignatureRegistry::new()), + func_data: Arc::new(FuncDataRegistry::new()), features: Arc::new(Default::default()), target: Arc::new(Default::default()), engine_id: EngineId::default(), @@ -40,6 +45,11 @@ impl DummyEngine { pub fn features(&self) -> &Features { &self.features } + + /// Shared func metadata registry. + pub(crate) fn func_data(&self) -> &Arc { + &self.func_data + } } impl Engine for DummyEngine { @@ -53,6 +63,10 @@ impl Engine for DummyEngine { self.signatures.register(func_type) } + fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef { + self.func_data.register(func_data) + } + /// Lookup a signature fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option { self.signatures.lookup(sig) diff --git a/tests/lib/wast/Cargo.toml b/tests/lib/wast/Cargo.toml index 0b4719695..7d7a567c6 100644 --- a/tests/lib/wast/Cargo.toml +++ b/tests/lib/wast/Cargo.toml @@ -12,7 +12,7 @@ edition = "2018" [dependencies] anyhow = "1.0" -wasmer = { path = "../../../lib/api", version = "1.0.2", default-features = false } +wasmer = { path = "../../../lib/api", version = "1.0.2", default-features = false, features = ["experimental-reference-types-extern-ref"] } wasmer-wasi = { path = "../../../lib/wasi", version = "1.0.2" } wast = "24.0" serde = "1" diff --git a/tests/lib/wast/src/spectest.rs b/tests/lib/wast/src/spectest.rs index 7e956c713..28d13995d 100644 --- a/tests/lib/wast/src/spectest.rs +++ b/tests/lib/wast/src/spectest.rs @@ -23,7 +23,7 @@ pub fn spectest_importobject(store: &Store) -> ImportObject { let global_f64 = Global::new(store, Val::F64(f64::from_bits(0x4084_d000_0000_0000))); let ty = TableType::new(ValType::FuncRef, 10, Some(20)); - let table = Table::new(store, ty, Val::ExternRef(ExternRef::Null)).unwrap(); + let table = Table::new(store, ty, Val::FuncRef(None)).unwrap(); let ty = MemoryType::new(1, Some(2), false); let memory = Memory::new(store, ty).unwrap(); diff --git a/tests/lib/wast/src/wast.rs b/tests/lib/wast/src/wast.rs index 9aa71c992..c9d3db8fc 100644 --- a/tests/lib/wast/src/wast.rs +++ b/tests/lib/wast/src/wast.rs @@ -1,7 +1,7 @@ use crate::error::{DirectiveError, DirectiveErrors}; use crate::spectest::spectest_importobject; use anyhow::{anyhow, bail, Result}; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::path::Path; use std::str; use wasmer::*; @@ -23,6 +23,9 @@ pub struct Wast { match_trap_messages: HashMap, /// If the current module was an allowed failure, we allow test to fail current_is_allowed_failure: bool, + /// Extern-ref manager: used for testing extern refs: they're referred to by + /// number in WAST, so we map here. + extern_refs: BTreeMap, /// The wasm Store store: Store, /// A flag indicating if Wast tests should stop as soon as one test fails. @@ -43,6 +46,7 @@ impl Wast { match_trap_messages: HashMap::new(), current_is_allowed_failure: false, instances: HashMap::new(), + extern_refs: BTreeMap::new(), fail_fast: true, disable_assert_trap_exhaustion: false, } @@ -94,10 +98,7 @@ impl Wast { wast::WastExecute::Module(mut module) => { let binary = module.encode()?; let result = self.instantiate(&binary); - match result { - Ok(_) => Ok(Vec::new()), - Err(e) => Err(e), - } + result.map(|_| Vec::new()) } wast::WastExecute::Get { module, global } => self.get(module.map(|s| s.name()), global), } @@ -107,7 +108,7 @@ impl Wast { let values = exec .args .iter() - .map(Self::runtime_value) + .map(|a| self.runtime_value(a)) .collect::>>()?; self.invoke(exec.module.map(|i| i.name()), exec.name, &values) } @@ -119,7 +120,7 @@ impl Wast { ) -> Result<()> { let values = result?; for (v, e) in values.iter().zip(results) { - if val_matches(v, e)? { + if self.val_matches(v, e)? { continue; } if let Val::V128(bits) = v { @@ -389,7 +390,7 @@ impl Wast { } /// Translate from a `script::Value` to a `Val`. - fn runtime_value(v: &wast::Expression<'_>) -> Result { + fn runtime_value(&mut self, v: &wast::Expression<'_>) -> Result { use wast::Instruction::*; if v.instrs.len() != 1 { @@ -401,6 +402,15 @@ impl Wast { F32Const(x) => Val::F32(f32::from_bits(x.bits)), F64Const(x) => Val::F64(f64::from_bits(x.bits)), V128Const(x) => Val::V128(u128::from_le_bytes(x.to_le_bytes())), + RefNull(wast::HeapType::Func) => Val::FuncRef(None), + RefNull(wast::HeapType::Extern) => Val::null(), + RefExtern(number) => { + let extern_ref = self + .extern_refs + .entry(*number) + .or_insert_with(|| ExternRef::new(*number)); + Val::ExternRef(extern_ref.clone()) + } other => bail!("couldn't convert {:?} to a runtime value", other), }) } @@ -437,6 +447,47 @@ impl Wast { .map(|alternative| actual.contains(alternative)) .unwrap_or(false) } + + fn val_matches(&self, actual: &Val, expected: &wast::AssertExpression) -> Result { + Ok(match (actual, expected) { + (Val::I32(a), wast::AssertExpression::I32(b)) => a == b, + (Val::I64(a), wast::AssertExpression::I64(b)) => a == b, + // Note that these float comparisons are comparing bits, not float + // values, so we're testing for bit-for-bit equivalence + (Val::F32(a), wast::AssertExpression::F32(b)) => f32_matches(*a, b), + (Val::F64(a), wast::AssertExpression::F64(b)) => f64_matches(*a, b), + (Val::V128(a), wast::AssertExpression::V128(b)) => v128_matches(*a, b), + (Val::FuncRef(None), wast::AssertExpression::RefNull(Some(wast::HeapType::Func))) => { + true + } + (Val::FuncRef(Some(_)), wast::AssertExpression::RefNull(_)) => false, + (Val::FuncRef(None), wast::AssertExpression::RefFunc(None)) => true, + (Val::FuncRef(None), wast::AssertExpression::RefFunc(Some(_))) => false, + ( + Val::ExternRef(extern_ref), + wast::AssertExpression::RefNull(Some(wast::HeapType::Extern)), + ) if extern_ref.is_null() => true, + (Val::ExternRef(extern_ref), wast::AssertExpression::RefExtern(_)) + if extern_ref.is_null() => + { + false + } + + (Val::ExternRef(_), wast::AssertExpression::RefNull(_)) => false, + (Val::ExternRef(extern_ref), wast::AssertExpression::RefExtern(num)) => { + if let Some(stored_extern_ref) = self.extern_refs.get(num) { + extern_ref == stored_extern_ref + } else { + false + } + } + _ => bail!( + "don't know how to compare {:?} and {:?} yet", + actual, + expected + ), + }) + } } fn extract_lane_as_i8(bytes: u128, lane: usize) -> i8 { @@ -455,23 +506,6 @@ fn extract_lane_as_i64(bytes: u128, lane: usize) -> i64 { (bytes >> (lane * 64)) as i64 } -fn val_matches(actual: &Val, expected: &wast::AssertExpression) -> Result { - Ok(match (actual, expected) { - (Val::I32(a), wast::AssertExpression::I32(b)) => a == b, - (Val::I64(a), wast::AssertExpression::I64(b)) => a == b, - // Note that these float comparisons are comparing bits, not float - // values, so we're testing for bit-for-bit equivalence - (Val::F32(a), wast::AssertExpression::F32(b)) => f32_matches(*a, b), - (Val::F64(a), wast::AssertExpression::F64(b)) => f64_matches(*a, b), - (Val::V128(a), wast::AssertExpression::V128(b)) => v128_matches(*a, b), - _ => bail!( - "don't know how to compare {:?} and {:?} yet", - actual, - expected - ), - }) -} - fn f32_matches(actual: f32, expected: &wast::NanPattern) -> bool { match expected { wast::NanPattern::CanonicalNan => actual.is_canonical_nan(), diff --git a/tests/wast/spec/imports.wast b/tests/wast/spec/imports.wast index 187e0afb9..cfe794894 100644 --- a/tests/wast/spec/imports.wast +++ b/tests/wast/spec/imports.wast @@ -12,8 +12,9 @@ (global (export "global-f32") f32 (f32.const 44)) (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 "table-10-20") 10 20 funcref) (memory (export "memory-2-inf") 2) + ;; Multiple memories are not yet supported ;; (memory (export "memory-2-4") 2 4) ) @@ -321,11 +322,11 @@ (module (type (func (result i32))) - (import "spectest" "table" (table 10 20 funcref)) - (elem 0 (i32.const 1) $f $g) + (import "spectest" "table" (table $tab 10 20 funcref)) + (elem (table $tab) (i32.const 1) func $f $g) (func (export "call") (param i32) (result i32) - (call_indirect (type 0) (local.get 0)) + (call_indirect $tab (type 0) (local.get 0)) ) (func $f (result i32) (i32.const 11)) (func $g (result i32) (i32.const 22)) @@ -340,11 +341,11 @@ (module (type (func (result i32))) - (table (import "spectest" "table") 10 20 funcref) - (elem 0 (i32.const 1) $f $g) + (table $tab (import "spectest" "table") 10 20 funcref) + (elem (table $tab) (i32.const 1) func $f $g) (func (export "call") (param i32) (result i32) - (call_indirect (type 0) (local.get 0)) + (call_indirect $tab (type 0) (local.get 0)) ) (func $f (result i32) (i32.const 11)) (func $g (result i32) (i32.const 22)) @@ -357,22 +358,25 @@ (assert_trap (invoke "call" (i32.const 100)) "undefined element") -(assert_invalid - (module (import "" "" (table 10 funcref)) (import "" "" (table 10 funcref))) - "multiple tables" -) -(assert_invalid - (module (import "" "" (table 10 funcref)) (table 10 funcref)) - "multiple tables" -) -(assert_invalid - (module (table 10 funcref) (table 10 funcref)) - "multiple tables" +(module + (import "spectest" "table" (table 0 funcref)) + (import "spectest" "table" (table 0 funcref)) + (table 10 funcref) + (table 10 funcref) ) (module (import "test" "table-10-inf" (table 10 funcref))) (module (import "test" "table-10-inf" (table 5 funcref))) (module (import "test" "table-10-inf" (table 0 funcref))) +(module (import "test" "table-10-20" (table 10 funcref))) +(module (import "test" "table-10-20" (table 5 funcref))) +(module (import "test" "table-10-20" (table 0 funcref))) +(module (import "test" "table-10-20" (table 10 20 funcref))) +(module (import "test" "table-10-20" (table 5 20 funcref))) +(module (import "test" "table-10-20" (table 0 20 funcref))) +(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 "spectest" "table" (table 10 funcref))) (module (import "spectest" "table" (table 5 funcref))) (module (import "spectest" "table" (table 0 funcref))) @@ -399,6 +403,14 @@ (module (import "test" "table-10-inf" (table 10 20 funcref))) "incompatible import type" ) +(assert_unlinkable + (module (import "test" "table-10-20" (table 12 20 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-20" (table 10 18 funcref))) + "incompatible import type" +) (assert_unlinkable (module (import "spectest" "table" (table 12 funcref))) "incompatible import type" @@ -431,7 +443,7 @@ (module (import "spectest" "memory" (memory 1 2)) - (data 0 (i32.const 10) "\10") + (data (memory 0) (i32.const 10) "\10") (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) ) @@ -443,7 +455,7 @@ (module (memory (import "spectest" "memory") 1 2) - (data 0 (i32.const 10) "\10") + (data (memory 0) (i32.const 10) "\10") (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) ) diff --git a/tests/wast/spec/ref_func.wast b/tests/wast/spec/ref_func.wast new file mode 100644 index 000000000..adb5cb788 --- /dev/null +++ b/tests/wast/spec/ref_func.wast @@ -0,0 +1,115 @@ +(module + (func (export "f") (param $x i32) (result i32) (local.get $x)) +) +(register "M") + +(module + (func $f (import "M" "f") (param i32) (result i32)) + (func $g (param $x i32) (result i32) + (i32.add (local.get $x) (i32.const 1)) + ) + + (global funcref (ref.func $f)) + (global funcref (ref.func $g)) + (global $v (mut funcref) (ref.func $f)) + + (global funcref (ref.func $gf1)) + (global funcref (ref.func $gf2)) + (func (drop (ref.func $ff1)) (drop (ref.func $ff2))) + (elem declare func $gf1 $ff1) + (elem declare funcref (ref.func $gf2) (ref.func $ff2)) + (func $gf1) + (func $gf2) + (func $ff1) + (func $ff2) + + (func (export "is_null-f") (result i32) + (ref.is_null (ref.func $f)) + ) + (func (export "is_null-g") (result i32) + (ref.is_null (ref.func $g)) + ) + (func (export "is_null-v") (result i32) + (ref.is_null (global.get $v)) + ) + + (func (export "set-f") (global.set $v (ref.func $f))) + (func (export "set-g") (global.set $v (ref.func $g))) + + (table $t 1 funcref) + (elem declare func $f $g) + + (func (export "call-f") (param $x i32) (result i32) + (table.set $t (i32.const 0) (ref.func $f)) + (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) + ) + (func (export "call-g") (param $x i32) (result i32) + (table.set $t (i32.const 0) (ref.func $g)) + (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) + ) + (func (export "call-v") (param $x i32) (result i32) + (table.set $t (i32.const 0) (global.get $v)) + (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) + ) +) + +(assert_return (invoke "is_null-f") (i32.const 0)) +(assert_return (invoke "is_null-g") (i32.const 0)) +(assert_return (invoke "is_null-v") (i32.const 0)) + +(assert_return (invoke "call-f" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "call-g" (i32.const 4)) (i32.const 5)) +(assert_return (invoke "call-v" (i32.const 4)) (i32.const 4)) +(invoke "set-g") +(assert_return (invoke "call-v" (i32.const 4)) (i32.const 5)) +(invoke "set-f") +(assert_return (invoke "call-v" (i32.const 4)) (i32.const 4)) + +(assert_invalid + (module + (func $f (import "M" "f") (param i32) (result i32)) + (func $g (import "M" "g") (param i32) (result i32)) + (global funcref (ref.func 7)) + ) + "unknown function 7" +) + + +;; Reference declaration + +(module + (func $f1) + (func $f2) + (func $f3) + (func $f4) + (func $f5) + (func $f6) + + (table $t 1 funcref) + + (global funcref (ref.func $f1)) + (export "f" (func $f2)) + (elem (table $t) (i32.const 0) func $f3) + (elem (table $t) (i32.const 0) funcref (ref.func $f4)) + (elem func $f5) + (elem funcref (ref.func $f6)) + + (func + (ref.func $f1) + (ref.func $f2) + (ref.func $f3) + (ref.func $f4) + (ref.func $f5) + (ref.func $f6) + (return) + ) +) + +(assert_invalid + (module (func $f (drop (ref.func $f)))) + "undeclared function reference" +) +(assert_invalid + (module (start $f) (func $f (drop (ref.func $f)))) + "undeclared function reference" +) diff --git a/tests/wast/spec/ref_is_null.wast b/tests/wast/spec/ref_is_null.wast new file mode 100644 index 000000000..8396da4a7 --- /dev/null +++ b/tests/wast/spec/ref_is_null.wast @@ -0,0 +1,58 @@ +(module + (func $f1 (export "funcref") (param $x funcref) (result i32) + (ref.is_null (local.get $x)) + ) + (func $f2 (export "externref") (param $x externref) (result i32) + (ref.is_null (local.get $x)) + ) + + (table $t1 2 funcref) + (table $t2 2 externref) + (elem (table $t1) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "init") (param $r externref) + (table.set $t2 (i32.const 1) (local.get $r)) + ) + (func (export "deinit") + (table.set $t1 (i32.const 1) (ref.null func)) + (table.set $t2 (i32.const 1) (ref.null extern)) + ) + + (func (export "funcref-elem") (param $x i32) (result i32) + (call $f1 (table.get $t1 (local.get $x))) + ) + (func (export "externref-elem") (param $x i32) (result i32) + (call $f2 (table.get $t2 (local.get $x))) + ) +) + +(assert_return (invoke "funcref" (ref.null func)) (i32.const 1)) +(assert_return (invoke "externref" (ref.null extern)) (i32.const 1)) + +(assert_return (invoke "externref" (ref.extern 1)) (i32.const 0)) + +(invoke "init" (ref.extern 0)) + +(assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) + +(assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 0)) + +(invoke "deinit") + +(assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) + +(assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 1)) + +(assert_invalid + (module (func $ref-vs-num (param i32) (ref.is_null (local.get 0)))) + "type mismatch" +) +(assert_invalid + (module (func $ref-vs-empty (ref.is_null))) + "type mismatch" +) diff --git a/tests/wast/spec/ref_null.wast b/tests/wast/spec/ref_null.wast new file mode 100644 index 000000000..67da24a61 --- /dev/null +++ b/tests/wast/spec/ref_null.wast @@ -0,0 +1,10 @@ +(module + (func (export "externref") (result externref) (ref.null extern)) + (func (export "funcref") (result funcref) (ref.null func)) + + (global externref (ref.null extern)) + (global funcref (ref.null func)) +) + +(assert_return (invoke "externref") (ref.null extern)) +(assert_return (invoke "funcref") (ref.null func)) \ No newline at end of file diff --git a/tests/wast/spec/table-sub.wast b/tests/wast/spec/table-sub.wast new file mode 100644 index 000000000..4f47de44a --- /dev/null +++ b/tests/wast/spec/table-sub.wast @@ -0,0 +1,21 @@ +(assert_invalid + (module + (table $t1 10 funcref) + (table $t2 10 externref) + (func $f + (table.copy $t1 $t2 (i32.const 0) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 10 funcref) + (elem $el externref) + (func $f + (table.init $t $el (i32.const 0) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) \ No newline at end of file diff --git a/tests/wast/spec/table.wast b/tests/wast/spec/table.wast index 0bc43ca60..0bd04f5cc 100644 --- a/tests/wast/spec/table.wast +++ b/tests/wast/spec/table.wast @@ -8,8 +8,8 @@ (module (table 0 65536 funcref)) (module (table 0 0xffff_ffff funcref)) -(assert_invalid (module (table 0 funcref) (table 0 funcref)) "multiple tables") -(assert_invalid (module (table (import "spectest" "table") 0 funcref) (table 0 funcref)) "multiple tables") +(module (table 0 funcref) (table 0 funcref)) +(module (table (import "spectest" "table") 0 funcref) (table 0 funcref)) (assert_invalid (module (elem (i32.const 0))) "unknown table") (assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table") diff --git a/tests/wast/spec/table_copy.wast b/tests/wast/spec/table_copy.wast new file mode 100644 index 000000000..380e84ee5 --- /dev/null +++ b/tests/wast/spec/table_copy.wast @@ -0,0 +1,3082 @@ +;; +;; Generated by ../meta/generate_table_copy.js +;; DO NOT EDIT THIS FILE. CHANGE THE SOURCE AND REGENERATE. +;; + +(module + (func (export "ef0") (result i32) (i32.const 0)) + (func (export "ef1") (result i32) (i32.const 1)) + (func (export "ef2") (result i32) (i32.const 2)) + (func (export "ef3") (result i32) (i32.const 3)) + (func (export "ef4") (result i32) (i32.const 4)) +) +(register "a") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (nop)) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 13) (i32.const 2) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 25) (i32.const 15) (i32.const 2))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 26)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 13) (i32.const 25) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check_t0" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 15)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 20) (i32.const 22) (i32.const 4))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 25) (i32.const 1) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 27)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 10) (i32.const 12) (i32.const 7))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 10)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 11)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 12) (i32.const 10) (i32.const 7))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 17)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 18)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t0 (i32.const 10) (i32.const 0) (i32.const 20))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 4)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 1)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 22)) (i32.const 7)) +(assert_return (invoke "check_t1" (i32.const 23)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 24)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 26)) (i32.const 6)) +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (nop)) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 13) (i32.const 2) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 25) (i32.const 15) (i32.const 2))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 26)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 13) (i32.const 25) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check_t0" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 15)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 20) (i32.const 22) (i32.const 4))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 25) (i32.const 1) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 27)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 10) (i32.const 12) (i32.const 7))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 10)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 11)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 12) (i32.const 10) (i32.const 7))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 17)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 18)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t1 (i32.const 10) (i32.const 0) (i32.const 20))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 4)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 1)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 22)) (i32.const 7)) +(assert_return (invoke "check_t1" (i32.const 23)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 24)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 26)) (i32.const 6)) +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 28) (i32.const 1) (i32.const 3)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 0xFFFFFFFE) (i32.const 1) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 15) (i32.const 25) (i32.const 6)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 15) (i32.const 0xFFFFFFFE) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 15) (i32.const 25) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 30) (i32.const 15) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 31) (i32.const 15) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 15) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 15) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 30) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 31) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 28) (i32.const 1) (i32.const 3)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 0xFFFFFFFE) (i32.const 1) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 15) (i32.const 25) (i32.const 6)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 15) (i32.const 0xFFFFFFFE) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 15) (i32.const 25) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 30) (i32.const 15) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 31) (i32.const 15) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 15) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 15) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 30) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 31) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 0) (i32.const 16)) + "out of bounds table access") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 23) (i32.const 0) (i32.const 15)) + "out of bounds table access") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 8)) (i32.const 8)) +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 24) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 23) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 23) (i32.const 15)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_return (invoke "test" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 8)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 11) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 11) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_return (invoke "test" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 12)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 14)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 16)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 17)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 18)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 11) (i32.const 24) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 21) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 21) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_return (invoke "test" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 22)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 23)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 21) (i32.const 24) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 21) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 21) (i32.const 21) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_return (invoke "test" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 22)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 23)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 10)) + +(module + (type (func (result i32))) + (table 128 128 funcref) + (elem (i32.const 112) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10 $f11 $f12 $f13 $f14 $f15) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 112) (i32.const 4294967264)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_return (invoke "test" (i32.const 112)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 113)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 114)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 115)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 116)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 117)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 118)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 119)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 120)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 121)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 122)) (i32.const 10)) +(assert_return (invoke "test" (i32.const 123)) (i32.const 11)) +(assert_return (invoke "test" (i32.const 124)) (i32.const 12)) +(assert_return (invoke "test" (i32.const 125)) (i32.const 13)) +(assert_return (invoke "test" (i32.const 126)) (i32.const 14)) +(assert_return (invoke "test" (i32.const 127)) (i32.const 15)) + +(module + (type (func (result i32))) + (table 128 128 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10 $f11 $f12 $f13 $f14 $f15) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 112) (i32.const 0) (i32.const 4294967264)) + "out of bounds table access") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 8)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 9)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "test" (i32.const 11)) (i32.const 11)) +(assert_return (invoke "test" (i32.const 12)) (i32.const 12)) +(assert_return (invoke "test" (i32.const 13)) (i32.const 13)) +(assert_return (invoke "test" (i32.const 14)) (i32.const 14)) +(assert_return (invoke "test" (i32.const 15)) (i32.const 15)) +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") diff --git a/tests/wast/spec/table_fill.wast b/tests/wast/spec/table_fill.wast new file mode 100644 index 000000000..3df64da1a --- /dev/null +++ b/tests/wast/spec/table_fill.wast @@ -0,0 +1,153 @@ +(module + (table $t 10 externref) + + (func (export "fill") (param $i i32) (param $r externref) (param $n i32) + (table.fill $t (local.get $i) (local.get $r) (local.get $n)) + ) + + (func (export "get") (param $i i32) (result externref) + (table.get $t (local.get $i)) + ) +) + +(assert_return (invoke "get" (i32.const 1)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 2)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 3)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 4)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 5)) (ref.null extern)) + +(assert_return (invoke "fill" (i32.const 2) (ref.extern 1) (i32.const 3))) +(assert_return (invoke "get" (i32.const 1)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 2)) (ref.extern 1)) +(assert_return (invoke "get" (i32.const 3)) (ref.extern 1)) +(assert_return (invoke "get" (i32.const 4)) (ref.extern 1)) +(assert_return (invoke "get" (i32.const 5)) (ref.null extern)) + +(assert_return (invoke "fill" (i32.const 4) (ref.extern 2) (i32.const 2))) +(assert_return (invoke "get" (i32.const 3)) (ref.extern 1)) +(assert_return (invoke "get" (i32.const 4)) (ref.extern 2)) +(assert_return (invoke "get" (i32.const 5)) (ref.extern 2)) +(assert_return (invoke "get" (i32.const 6)) (ref.null extern)) + +(assert_return (invoke "fill" (i32.const 4) (ref.extern 3) (i32.const 0))) +(assert_return (invoke "get" (i32.const 3)) (ref.extern 1)) +(assert_return (invoke "get" (i32.const 4)) (ref.extern 2)) +(assert_return (invoke "get" (i32.const 5)) (ref.extern 2)) + +(assert_return (invoke "fill" (i32.const 8) (ref.extern 4) (i32.const 2))) +(assert_return (invoke "get" (i32.const 7)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 8)) (ref.extern 4)) +(assert_return (invoke "get" (i32.const 9)) (ref.extern 4)) + +(assert_return (invoke "fill" (i32.const 9) (ref.null extern) (i32.const 1))) +(assert_return (invoke "get" (i32.const 8)) (ref.extern 4)) +(assert_return (invoke "get" (i32.const 9)) (ref.null extern)) + +(assert_return (invoke "fill" (i32.const 10) (ref.extern 5) (i32.const 0))) +(assert_return (invoke "get" (i32.const 9)) (ref.null extern)) + +(assert_trap + (invoke "fill" (i32.const 8) (ref.extern 6) (i32.const 3)) + "out of bounds table access" +) +(assert_return (invoke "get" (i32.const 7)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 8)) (ref.extern 4)) +(assert_return (invoke "get" (i32.const 9)) (ref.null extern)) + +(assert_trap + (invoke "fill" (i32.const 11) (ref.null extern) (i32.const 0)) + "out of bounds table access" +) + +(assert_trap + (invoke "fill" (i32.const 11) (ref.null extern) (i32.const 10)) + "out of bounds table access" +) + + +;; Type errors + +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-value-length-empty-vs-i32-i32 + (table.fill $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-empty-vs-i32 + (table.fill $t (ref.null extern) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-value-empty-vs + (table.fill $t (i32.const 1) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-length-empty-vs-i32 + (table.fill $t (i32.const 1) (ref.null extern)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-index-f32-vs-i32 + (table.fill $t (f32.const 1) (ref.null extern) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 funcref) + (func $type-value-vs-funcref (param $r externref) + (table.fill $t (i32.const 1) (local.get $r) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-length-f32-vs-i32 + (table.fill $t (i32.const 1) (ref.null extern) (f32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t1 1 externref) + (table $t2 1 funcref) + (func $type-value-externref-vs-funcref-multi (param $r externref) + (table.fill $t2 (i32.const 0) (local.get $r) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-empty-vs-num (result i32) + (table.fill $t (i32.const 0) (ref.null extern) (i32.const 1)) + ) + ) + "type mismatch" +) diff --git a/tests/wast/spec/table_get.wast b/tests/wast/spec/table_get.wast new file mode 100644 index 000000000..5d57c3198 --- /dev/null +++ b/tests/wast/spec/table_get.wast @@ -0,0 +1,88 @@ +(module + (table $t2 2 externref) + (table $t3 3 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "init") (param $r externref) + (table.set $t2 (i32.const 1) (local.get $r)) + (table.set $t3 (i32.const 2) (table.get $t3 (i32.const 1))) + ) + + (func (export "get-externref") (param $i i32) (result externref) + (table.get $t2 (local.get $i)) + ) + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) + + (func (export "is_null-funcref") (param $i i32) (result i32) + (ref.is_null (call $f3 (local.get $i))) + ) +) + +(invoke "init" (ref.extern 1)) + +(assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern)) +(assert_return (invoke "get-externref" (i32.const 1)) (ref.extern 1)) + +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) +(assert_return (invoke "is_null-funcref" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "is_null-funcref" (i32.const 2)) (i32.const 0)) + +(assert_trap (invoke "get-externref" (i32.const 2)) "out of bounds table access") +(assert_trap (invoke "get-funcref" (i32.const 3)) "out of bounds table access") +(assert_trap (invoke "get-externref" (i32.const -1)) "out of bounds table access") +(assert_trap (invoke "get-funcref" (i32.const -1)) "out of bounds table access") + + +;; Type errors + +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-empty-vs-i32 (result externref) + (table.get $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-f32-vs-i32 (result externref) + (table.get $t (f32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 10 externref) + (func $type-result-externref-vs-empty + (table.get $t (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-result-externref-vs-funcref (result funcref) + (table.get $t (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t1 1 funcref) + (table $t2 1 externref) + (func $type-result-externref-vs-funcref-multi (result funcref) + (table.get $t2 (i32.const 0)) + ) + ) + "type mismatch" +) diff --git a/tests/wast/spec/table_grow.wast b/tests/wast/spec/table_grow.wast new file mode 100644 index 000000000..7d5b5630f --- /dev/null +++ b/tests/wast/spec/table_grow.wast @@ -0,0 +1,173 @@ +(module + (table $t 0 externref) + + (func (export "get") (param $i i32) (result externref) (table.get $t (local.get $i))) + (func (export "set") (param $i i32) (param $r externref) (table.set $t (local.get $i) (local.get $r))) + + (func (export "grow") (param $sz i32) (param $init externref) (result i32) + (table.grow $t (local.get $init) (local.get $sz)) + ) + (func (export "size") (result i32) (table.size $t)) +) + +(assert_return (invoke "size") (i32.const 0)) +(assert_trap (invoke "set" (i32.const 0) (ref.extern 2)) "out of bounds table access") +(assert_trap (invoke "get" (i32.const 0)) "out of bounds table access") + +(assert_return (invoke "grow" (i32.const 1) (ref.null extern)) (i32.const 0)) +(assert_return (invoke "size") (i32.const 1)) +(assert_return (invoke "get" (i32.const 0)) (ref.null extern)) +(assert_return (invoke "set" (i32.const 0) (ref.extern 2))) +(assert_return (invoke "get" (i32.const 0)) (ref.extern 2)) +(assert_trap (invoke "set" (i32.const 1) (ref.extern 2)) "out of bounds table access") +(assert_trap (invoke "get" (i32.const 1)) "out of bounds table access") + +(assert_return (invoke "grow" (i32.const 4) (ref.extern 3)) (i32.const 1)) +(assert_return (invoke "size") (i32.const 5)) +(assert_return (invoke "get" (i32.const 0)) (ref.extern 2)) +(assert_return (invoke "set" (i32.const 0) (ref.extern 2))) +(assert_return (invoke "get" (i32.const 0)) (ref.extern 2)) +(assert_return (invoke "get" (i32.const 1)) (ref.extern 3)) +(assert_return (invoke "get" (i32.const 4)) (ref.extern 3)) +(assert_return (invoke "set" (i32.const 4) (ref.extern 4))) +(assert_return (invoke "get" (i32.const 4)) (ref.extern 4)) +(assert_trap (invoke "set" (i32.const 5) (ref.extern 2)) "out of bounds table access") +(assert_trap (invoke "get" (i32.const 5)) "out of bounds table access") + + +;; Reject growing to size outside i32 value range +(module + (table $t 0x10 funcref) + (elem declare func $f) + (func $f (export "grow") (result i32) + (table.grow $t (ref.func $f) (i32.const 0xffff_fff0)) + ) +) + +(assert_return (invoke "grow") (i32.const -1)) + + +(module + (table $t 0 externref) + (func (export "grow") (param i32) (result i32) + (table.grow $t (ref.null extern) (local.get 0)) + ) +) + +(assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 800)) (i32.const 3)) + + +(module + (table $t 0 10 externref) + (func (export "grow") (param i32) (result i32) + (table.grow $t (ref.null extern) (local.get 0)) + ) +) + +(assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "grow" (i32.const 6)) (i32.const 4)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 10)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1)) + + +(module + (table $t 10 funcref) + (func (export "grow") (param i32) (result i32) + (table.grow $t (ref.null func) (local.get 0)) + ) + (elem declare func 1) + (func (export "check-table-null") (param i32 i32) (result funcref) + (local funcref) + (local.set 2 (ref.func 1)) + (block + (loop + (local.set 2 (table.get $t (local.get 0))) + (br_if 1 (i32.eqz (ref.is_null (local.get 2)))) + (br_if 1 (i32.ge_u (local.get 0) (local.get 1))) + (local.set 0 (i32.add (local.get 0) (i32.const 1))) + (br_if 0 (i32.le_u (local.get 0) (local.get 1))) + ) + ) + (local.get 2) + ) +) + +(assert_return (invoke "check-table-null" (i32.const 0) (i32.const 9)) (ref.null func)) +(assert_return (invoke "grow" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "check-table-null" (i32.const 0) (i32.const 19)) (ref.null func)) + + +;; Type errors + +(assert_invalid + (module + (table $t 0 externref) + (func $type-init-size-empty-vs-i32-externref (result i32) + (table.grow $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-size-empty-vs-i32 (result i32) + (table.grow $t (ref.null extern)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-init-empty-vs-externref (result i32) + (table.grow $t (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-size-f32-vs-i32 (result i32) + (table.grow $t (ref.null extern) (f32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 funcref) + (func $type-init-externref-vs-funcref (param $r externref) (result i32) + (table.grow $t (local.get $r) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-i32-vs-empty + (table.grow $t (ref.null extern) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-i32-vs-f32 (result f32) + (table.grow $t (ref.null extern) (i32.const 0)) + ) + ) + "type mismatch" +) diff --git a/tests/wast/spec/table_init.wast b/tests/wast/spec/table_init.wast new file mode 100644 index 000000000..0b2d26f77 --- /dev/null +++ b/tests/wast/spec/table_init.wast @@ -0,0 +1,2143 @@ +;; +;; Generated by ../meta/generate_table_init.js +;; DO NOT EDIT THIS FILE. CHANGE THE SOURCE AND REGENERATE. +;; + +(module + (func (export "ef0") (result i32) (i32.const 0)) + (func (export "ef1") (result i32) (i32.const 1)) + (func (export "ef2") (result i32) (i32.const 2)) + (func (export "ef3") (result i32) (i32.const 3)) + (func (export "ef4") (result i32) (i32.const 4)) +) +(register "a") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t0 1 (i32.const 7) (i32.const 0) (i32.const 4))) + (func (export "check") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t0 3 (i32.const 15) (i32.const 1) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 9)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t0 1 (i32.const 7) (i32.const 0) (i32.const 4)) + (elem.drop 1) + (table.init $t0 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (elem.drop 3) + (table.copy $t0 0 (i32.const 20) (i32.const 15) (i32.const 5)) + (table.copy $t0 0 (i32.const 21) (i32.const 29) (i32.const 1)) + (table.copy $t0 0 (i32.const 24) (i32.const 10) (i32.const 1)) + (table.copy $t0 0 (i32.const 13) (i32.const 11) (i32.const 4)) + (table.copy $t0 0 (i32.const 19) (i32.const 20) (i32.const 5))) + (func (export "check") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_return (invoke "check" (i32.const 19)) (i32.const 9)) +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_return (invoke "check" (i32.const 21)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_return (invoke "check" (i32.const 23)) (i32.const 8)) +(assert_return (invoke "check" (i32.const 24)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t1 1 (i32.const 7) (i32.const 0) (i32.const 4))) + (func (export "check") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t1 3 (i32.const 15) (i32.const 1) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 9)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t1 1 (i32.const 7) (i32.const 0) (i32.const 4)) + (elem.drop 1) + (table.init $t1 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (elem.drop 3) + (table.copy $t1 1 (i32.const 20) (i32.const 15) (i32.const 5)) + (table.copy $t1 1 (i32.const 21) (i32.const 29) (i32.const 1)) + (table.copy $t1 1 (i32.const 24) (i32.const 10) (i32.const 1)) + (table.copy $t1 1 (i32.const 13) (i32.const 11) (i32.const 4)) + (table.copy $t1 1 (i32.const 19) (i32.const 20) (i32.const 5))) + (func (export "check") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_return (invoke "check" (i32.const 19)) (i32.const 9)) +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_return (invoke "check" (i32.const 21)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_return (invoke "check" (i32.const 23)) (i32.const 8)) +(assert_return (invoke "check" (i32.const 24)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") +(assert_invalid + (module + (func (export "test") + (elem.drop 0))) + "unknown elem segment 0") + +(assert_invalid + (module + (func (export "test") + (table.init 0 (i32.const 12) (i32.const 1) (i32.const 1)))) + "unknown table 0") + +(assert_invalid + (module + (elem funcref (ref.func 0)) + (func (result i32) (i32.const 0)) + (func (export "test") + (elem.drop 4))) + "unknown elem segment 4") + +(assert_invalid + (module + (elem funcref (ref.func 0)) + (func (result i32) (i32.const 0)) + (func (export "test") + (table.init 4 (i32.const 12) (i32.const 1) (i32.const 1)))) + "unknown table 0") + + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 2) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 2 (i32.const 12) (i32.const 1) (i32.const 1)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 1) (i32.const 1)) + (table.init 1 (i32.const 21) (i32.const 1) (i32.const 1)))) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 1) + (elem.drop 1))) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 1) + (table.init 1 (i32.const 12) (i32.const 1) (i32.const 1)))) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 0) (i32.const 5)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 2) (i32.const 3)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 28) (i32.const 1) (i32.const 3)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 12) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 12) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 30) (i32.const 2) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 31) (i32.const 2) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 30) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.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 funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 31) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 26) (i32.const 1) (i32.const 3)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 12) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 12) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 28) (i32.const 2) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 29) (i32.const 2) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 28) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.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)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 29) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 24) (i32.const 16)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 25) (i32.const 16)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 160 320 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 96) (i32.const 32)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 128)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 129)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 130)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 131)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 132)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 133)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 134)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 135)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 136)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 137)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 138)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 139)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 140)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 141)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 142)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 143)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 144)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 145)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 146)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 147)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 148)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 149)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 150)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 151)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 152)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 153)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 154)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 155)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 156)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 157)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 158)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 159)) "uninitialized element") + +(module + (type (func (result i32))) + (table 160 320 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 97) (i32.const 31)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 128)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 129)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 130)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 131)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 132)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 133)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 134)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 135)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 136)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 137)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 138)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 139)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 140)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 141)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 142)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 143)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 144)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 145)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 146)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 147)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 148)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 149)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 150)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 151)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 152)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 153)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 154)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 155)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 156)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 157)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 158)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 159)) "uninitialized element") + +(module + (type (func (result i32))) + (table 64 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 48) (i32.const 4294967280)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") + +(module + (type (func (result i32))) + (table 16 16 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 8) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 0) (i32.const 4294967292)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") + +(module + (table 1 funcref) + ;; 65 elem segments. 64 is the smallest positive number that is encoded + ;; differently as a signed LEB. + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) + (func (table.init 64 (i32.const 0) (i32.const 0) (i32.const 0)))) diff --git a/tests/wast/spec/table_set.wast b/tests/wast/spec/table_set.wast new file mode 100644 index 000000000..5a9cfa371 --- /dev/null +++ b/tests/wast/spec/table_set.wast @@ -0,0 +1,119 @@ +(module + (table $t2 1 externref) + (table $t3 2 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "get-externref") (param $i i32) (result externref) + (table.get $t2 (local.get $i)) + ) + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) + + (func (export "set-externref") (param $i i32) (param $r externref) + (table.set $t2 (local.get $i) (local.get $r)) + ) + (func (export "set-funcref") (param $i i32) (param $r funcref) + (table.set $t3 (local.get $i) (local.get $r)) + ) + (func (export "set-funcref-from") (param $i i32) (param $j i32) + (table.set $t3 (local.get $i) (table.get $t3 (local.get $j))) + ) + + (func (export "is_null-funcref") (param $i i32) (result i32) + (ref.is_null (call $f3 (local.get $i))) + ) +) + +(assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern)) +(assert_return (invoke "set-externref" (i32.const 0) (ref.extern 1))) +(assert_return (invoke "get-externref" (i32.const 0)) (ref.extern 1)) +(assert_return (invoke "set-externref" (i32.const 0) (ref.null extern))) +(assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern)) + +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) +(assert_return (invoke "set-funcref-from" (i32.const 0) (i32.const 1))) +(assert_return (invoke "is_null-funcref" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "set-funcref" (i32.const 0) (ref.null func))) +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) + +(assert_trap (invoke "set-externref" (i32.const 2) (ref.null extern)) "out of bounds table access") +(assert_trap (invoke "set-funcref" (i32.const 3) (ref.null func)) "out of bounds table access") +(assert_trap (invoke "set-externref" (i32.const -1) (ref.null extern)) "out of bounds table access") +(assert_trap (invoke "set-funcref" (i32.const -1) (ref.null func)) "out of bounds table access") + +(assert_trap (invoke "set-externref" (i32.const 2) (ref.extern 0)) "out of bounds table access") +(assert_trap (invoke "set-funcref-from" (i32.const 3) (i32.const 1)) "out of bounds table access") +(assert_trap (invoke "set-externref" (i32.const -1) (ref.extern 0)) "out of bounds table access") +(assert_trap (invoke "set-funcref-from" (i32.const -1) (i32.const 1)) "out of bounds table access") + + +;; Type errors + +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-value-empty-vs-i32-externref + (table.set $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-empty-vs-i32 + (table.set $t (ref.null extern)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-value-empty-vs-externref + (table.set $t (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-size-f32-vs-i32 + (table.set $t (f32.const 1) (ref.null extern)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 funcref) + (func $type-value-externref-vs-funcref (param $r externref) + (table.set $t (i32.const 1) (local.get $r)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t1 1 externref) + (table $t2 1 funcref) + (func $type-value-externref-vs-funcref-multi (param $r externref) + (table.set $t2 (i32.const 0) (local.get $r)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 10 externref) + (func $type-result-empty-vs-num (result i32) + (table.set $t (i32.const 0) (ref.null extern)) + ) + ) + "type mismatch" +) diff --git a/tests/wast/spec/table_size.wast b/tests/wast/spec/table_size.wast new file mode 100644 index 000000000..ad293b5ee --- /dev/null +++ b/tests/wast/spec/table_size.wast @@ -0,0 +1,86 @@ +(module + (table $t0 0 externref) + (table $t1 1 externref) + (table $t2 0 2 externref) + (table $t3 3 8 externref) + + (func (export "size-t0") (result i32) (table.size $t0)) + (func (export "size-t1") (result i32) (table.size $t1)) + (func (export "size-t2") (result i32) (table.size $t2)) + (func (export "size-t3") (result i32) (table.size $t3)) + + (func (export "grow-t0") (param $sz i32) + (drop (table.grow $t0 (ref.null extern) (local.get $sz))) + ) + (func (export "grow-t1") (param $sz i32) + (drop (table.grow $t1 (ref.null extern) (local.get $sz))) + ) + (func (export "grow-t2") (param $sz i32) + (drop (table.grow $t2 (ref.null extern) (local.get $sz))) + ) + (func (export "grow-t3") (param $sz i32) + (drop (table.grow $t3 (ref.null extern) (local.get $sz))) + ) +) + +(assert_return (invoke "size-t0") (i32.const 0)) +(assert_return (invoke "grow-t0" (i32.const 1))) +(assert_return (invoke "size-t0") (i32.const 1)) +(assert_return (invoke "grow-t0" (i32.const 4))) +(assert_return (invoke "size-t0") (i32.const 5)) +(assert_return (invoke "grow-t0" (i32.const 0))) +(assert_return (invoke "size-t0") (i32.const 5)) + +(assert_return (invoke "size-t1") (i32.const 1)) +(assert_return (invoke "grow-t1" (i32.const 1))) +(assert_return (invoke "size-t1") (i32.const 2)) +(assert_return (invoke "grow-t1" (i32.const 4))) +(assert_return (invoke "size-t1") (i32.const 6)) +(assert_return (invoke "grow-t1" (i32.const 0))) +(assert_return (invoke "size-t1") (i32.const 6)) + +(assert_return (invoke "size-t2") (i32.const 0)) +(assert_return (invoke "grow-t2" (i32.const 3))) +(assert_return (invoke "size-t2") (i32.const 0)) +(assert_return (invoke "grow-t2" (i32.const 1))) +(assert_return (invoke "size-t2") (i32.const 1)) +(assert_return (invoke "grow-t2" (i32.const 0))) +(assert_return (invoke "size-t2") (i32.const 1)) +(assert_return (invoke "grow-t2" (i32.const 4))) +(assert_return (invoke "size-t2") (i32.const 1)) +(assert_return (invoke "grow-t2" (i32.const 1))) +(assert_return (invoke "size-t2") (i32.const 2)) + +(assert_return (invoke "size-t3") (i32.const 3)) +(assert_return (invoke "grow-t3" (i32.const 1))) +(assert_return (invoke "size-t3") (i32.const 4)) +(assert_return (invoke "grow-t3" (i32.const 3))) +(assert_return (invoke "size-t3") (i32.const 7)) +(assert_return (invoke "grow-t3" (i32.const 0))) +(assert_return (invoke "size-t3") (i32.const 7)) +(assert_return (invoke "grow-t3" (i32.const 2))) +(assert_return (invoke "size-t3") (i32.const 7)) +(assert_return (invoke "grow-t3" (i32.const 1))) +(assert_return (invoke "size-t3") (i32.const 8)) + + +;; Type errors + +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-i32-vs-empty + (table.size $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-i32-vs-f32 (result f32) + (table.size $t) + ) + ) + "type mismatch" +) diff --git a/tests/wast/wasmer/rotate-shift-overflow.wast b/tests/wast/wasmer/rotate-shift-overflow.wast index fa2efee24..456ea414f 100644 --- a/tests/wast/wasmer/rotate-shift-overflow.wast +++ b/tests/wast/wasmer/rotate-shift-overflow.wast @@ -3,168 +3,332 @@ ;; assert_return line hides the bug. (module - ;; shl - (func (export "shl1") (result i32) + ;; shl i32 + (func (export "shl1_i32") (result i32) i32.const 235 i32.const 0 i32.shl ) - (func (export "shl2") (result i32) + (func (export "shl2_i32") (result i32) i32.const 235 i32.const 32 i32.shl ) - (func (export "shl3") (result i32) + (func (export "shl3_i32") (result i32) i32.const 235 i32.const 100 i32.shl ) - (func (export "shl4") (result i32) + (func (export "shl4_i32") (result i32) i32.const 235 i32.const -32 i32.shl ) - (func (export "shl5") (result i32) + (func (export "shl5_i32") (result i32) i32.const 235 i32.const -100 i32.shl ) + ;; shl i64 + (func (export "shl1_i64") (result i64) + i64.const 235 + i64.const 0 + i64.shl + ) + (func (export "shl2_i64") (result i64) + i64.const 235 + i64.const 64 + i64.shl + ) + (func (export "shl3_i64") (result i64) + i64.const 235 + i64.const 100 + i64.shl + ) + (func (export "shl4_i64") (result i64) + i64.const 235 + i64.const -64 + i64.shl + ) + (func (export "shl5_i64") (result i64) + i64.const 235 + i64.const -100 + i64.shl + ) - ;; shr_u - (func (export "shr_u1") (result i32) + ;; shr_u i32 + (func (export "shr_u1_i32") (result i32) i32.const 235 i32.const 0 i32.shr_u ) - (func (export "shr_u2") (result i32) + (func (export "shr_u2_i32") (result i32) i32.const 235 i32.const 32 i32.shr_u ) - (func (export "shr_u3") (result i32) + (func (export "shr_u3_i32") (result i32) i32.const 235 i32.const 100 i32.shr_u ) - (func (export "shr_u4") (result i32) + (func (export "shr_u4_i32") (result i32) i32.const 235 i32.const -32 i32.shr_u ) - (func (export "shr_u5") (result i32) + (func (export "shr_u5_i32") (result i32) i32.const 235 i32.const -100 i32.shr_u ) - ;; shr_s - (func (export "shr_s1") (result i32) + ;; shr_u i64 + (func (export "shr_u1_i64") (result i64) + i64.const 235 + i64.const 0 + i64.shr_u + ) + (func (export "shr_u2_i64") (result i64) + i64.const 235 + i64.const 64 + i64.shr_u + ) + (func (export "shr_u3_i64") (result i64) + i64.const 235 + i64.const 100 + i64.shr_u + ) + (func (export "shr_u4_i64") (result i64) + i64.const 235 + i64.const -64 + i64.shr_u + ) + (func (export "shr_u5_i64") (result i64) + i64.const 235 + i64.const -100 + i64.shr_u + ) + + ;; shr_s i32 + (func (export "shr_s1_i32") (result i32) i32.const 235 i32.const 0 i32.shr_s ) - (func (export "shr_s2") (result i32) + (func (export "shr_s2_i32") (result i32) i32.const 235 i32.const 32 i32.shr_s ) - (func (export "shr_s3") (result i32) + (func (export "shr_s3_i32") (result i32) i32.const 235 i32.const 100 i32.shr_s ) - (func (export "shr_s4") (result i32) + (func (export "shr_s4_i32") (result i32) i32.const 235 i32.const -32 i32.shr_s ) - (func (export "shr_s5") (result i32) + (func (export "shr_s5_i32") (result i32) i32.const 235 i32.const -100 i32.shr_s ) - ;; rotl - (func (export "rotl1") (result i32) + ;; shr_s i64 + (func (export "shr_s1_i64") (result i64) + i64.const 235 + i64.const 0 + i64.shr_s + ) + (func (export "shr_s2_i64") (result i64) + i64.const 235 + i64.const 64 + i64.shr_s + ) + (func (export "shr_s3_i64") (result i64) + i64.const 235 + i64.const 100 + i64.shr_s + ) + (func (export "shr_s4_i64") (result i64) + i64.const 235 + i64.const -64 + i64.shr_s + ) + (func (export "shr_s5_i64") (result i64) + i64.const 235 + i64.const -100 + i64.shr_s + ) + + ;; rotl i32 + (func (export "rotl1_i32") (result i32) i32.const 235 i32.const 0 i32.rotl ) - (func (export "rotl2") (result i32) + (func (export "rotl2_i32") (result i32) i32.const 235 i32.const 32 i32.rotl ) - (func (export "rotl3") (result i32) + (func (export "rotl3_i32") (result i32) i32.const 235 i32.const 100 i32.rotl ) - (func (export "rotl4") (result i32) + (func (export "rotl4_i32") (result i32) i32.const 235 i32.const -32 i32.rotl ) - (func (export "rotl5") (result i32) + (func (export "rotl5_i32") (result i32) i32.const 235 i32.const -100 i32.rotl ) - ;; rotr - (func (export "rotr1") (result i32) + ;; rotl i64 + (func (export "rotl1_i64") (result i64) + i64.const 235 + i64.const 0 + i64.rotl + ) + (func (export "rotl2_i64") (result i64) + i64.const 235 + i64.const 64 + i64.rotl + ) + (func (export "rotl3_i64") (result i64) + i64.const 235 + i64.const 100 + i64.rotl + ) + (func (export "rotl4_i64") (result i64) + i64.const 235 + i64.const -64 + i64.rotl + ) + (func (export "rotl5_i64") (result i64) + i64.const 235 + i64.const -100 + i64.rotl + ) + + ;; rotr i32 + (func (export "rotr1_i32") (result i32) i32.const 235 i32.const 0 i32.rotr ) - (func (export "rotr2") (result i32) + (func (export "rotr2_i32") (result i32) i32.const 235 i32.const 32 i32.rotr ) - (func (export "rotr3") (result i32) + (func (export "rotr3_i32") (result i32) i32.const 235 i32.const 100 i32.rotr ) - (func (export "rotr4") (result i32) + (func (export "rotr4_i32") (result i32) i32.const 235 i32.const -32 i32.rotr ) - (func (export "rotr5") (result i32) + (func (export "rotr5_i32") (result i32) i32.const 235 i32.const -100 i32.rotr ) + + ;; rotr i64 + (func (export "rotr1_i64") (result i64) + i64.const 235 + i64.const 0 + i64.rotr + ) + (func (export "rotr2_i64") (result i64) + i64.const 235 + i64.const 64 + i64.rotr + ) + (func (export "rotr3_i64") (result i64) + i64.const 235 + i64.const 100 + i64.rotr + ) + (func (export "rotr4_i64") (result i64) + i64.const 235 + i64.const -64 + i64.rotr + ) + (func (export "rotr5_i64") (result i64) + i64.const 235 + i64.const -100 + i64.rotr + ) ) -(assert_return (invoke "shl1") (i32.const 235)) -(assert_return (invoke "shl2") (i32.const 235)) -(assert_return (invoke "shl3") (i32.const 3760)) -(assert_return (invoke "shl4") (i32.const 235)) -(assert_return (invoke "shl5") (i32.const -1342177280)) +(assert_return (invoke "shl1_i32") (i32.const 235)) +(assert_return (invoke "shl2_i32") (i32.const 235)) +(assert_return (invoke "shl3_i32") (i32.const 3760)) +(assert_return (invoke "shl4_i32") (i32.const 235)) +(assert_return (invoke "shl5_i32") (i32.const -1342177280)) -(assert_return (invoke "shr_u1") (i32.const 235)) -(assert_return (invoke "shr_u2") (i32.const 235)) -(assert_return (invoke "shr_u3") (i32.const 14)) -(assert_return (invoke "shr_u4") (i32.const 235)) -(assert_return (invoke "shr_u5") (i32.const 0)) +(assert_return (invoke "shl1_i64") (i64.const 235)) +(assert_return (invoke "shl2_i64") (i64.const 235)) +(assert_return (invoke "shl3_i64") (i64.const 16149077032960)) +(assert_return (invoke "shl4_i64") (i64.const 235)) +(assert_return (invoke "shl5_i64") (i64.const 63082332160)) -(assert_return (invoke "shr_s1") (i32.const 235)) -(assert_return (invoke "shr_s2") (i32.const 235)) -(assert_return (invoke "shr_s3") (i32.const 14)) -(assert_return (invoke "shr_s4") (i32.const 235)) -(assert_return (invoke "shr_s5") (i32.const 0)) +(assert_return (invoke "shr_u1_i32") (i32.const 235)) +(assert_return (invoke "shr_u2_i32") (i32.const 235)) +(assert_return (invoke "shr_u3_i32") (i32.const 14)) +(assert_return (invoke "shr_u4_i32") (i32.const 235)) +(assert_return (invoke "shr_u5_i32") (i32.const 0)) -(assert_return (invoke "rotl1") (i32.const 235)) -(assert_return (invoke "rotl2") (i32.const 235)) -(assert_return (invoke "rotl3") (i32.const 3760)) -(assert_return (invoke "rotl4") (i32.const 235)) -(assert_return (invoke "rotl5") (i32.const -1342177266)) +(assert_return (invoke "shr_u1_i64") (i64.const 235)) +(assert_return (invoke "shr_u2_i64") (i64.const 235)) +(assert_return (invoke "shr_u3_i64") (i64.const 0)) +(assert_return (invoke "shr_u4_i64") (i64.const 235)) +(assert_return (invoke "shr_u5_i64") (i64.const 0)) -(assert_return (invoke "rotr1") (i32.const 235)) -(assert_return (invoke "rotr2") (i32.const 235)) -(assert_return (invoke "rotr3") (i32.const -1342177266)) -(assert_return (invoke "rotr4") (i32.const 235)) -(assert_return (invoke "rotr5") (i32.const 3760)) +(assert_return (invoke "shr_s1_i32") (i32.const 235)) +(assert_return (invoke "shr_s2_i32") (i32.const 235)) +(assert_return (invoke "shr_s3_i32") (i32.const 14)) +(assert_return (invoke "shr_s4_i32") (i32.const 235)) +(assert_return (invoke "shr_s5_i32") (i32.const 0)) + +(assert_return (invoke "shr_s1_i64") (i64.const 235)) +(assert_return (invoke "shr_s2_i64") (i64.const 235)) +(assert_return (invoke "shr_s3_i64") (i64.const 0)) +(assert_return (invoke "shr_s4_i64") (i64.const 235)) +(assert_return (invoke "shr_s5_i64") (i64.const 0)) + +(assert_return (invoke "rotl1_i32") (i32.const 235)) +(assert_return (invoke "rotl2_i32") (i32.const 235)) +(assert_return (invoke "rotl3_i32") (i32.const 3760)) +(assert_return (invoke "rotl4_i32") (i32.const 235)) +(assert_return (invoke "rotl5_i32") (i32.const -1342177266)) + +(assert_return (invoke "rotl1_i64") (i64.const 235)) +(assert_return (invoke "rotl2_i64") (i64.const 235)) +(assert_return (invoke "rotl3_i64") (i64.const 16149077032960)) +(assert_return (invoke "rotl4_i64") (i64.const 235)) +(assert_return (invoke "rotl5_i64") (i64.const 63082332160)) + +(assert_return (invoke "rotr1_i32") (i32.const 235)) +(assert_return (invoke "rotr2_i32") (i32.const 235)) +(assert_return (invoke "rotr3_i32") (i32.const -1342177266)) +(assert_return (invoke "rotr4_i32") (i32.const 235)) +(assert_return (invoke "rotr5_i32") (i32.const 3760)) + +(assert_return (invoke "rotr1_i64") (i64.const 235)) +(assert_return (invoke "rotr2_i64") (i64.const 235)) +(assert_return (invoke "rotr3_i64") (i64.const 63082332160)) +(assert_return (invoke "rotr4_i64") (i64.const 235)) +(assert_return (invoke "rotr5_i64") (i64.const 16149077032960))