mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-29 16:41:33 +00:00
Merge branch 'master' into cranelift-upgrade
# Conflicts: # src/webassembly/module.rs
This commit is contained in:
@ -1,6 +1,26 @@
|
|||||||
version: 2
|
version: 2
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
# Job used for testing
|
||||||
|
test:
|
||||||
|
docker:
|
||||||
|
- image: circleci/rust:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- v4-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||||
|
- run: sudo apt-get install -y cmake
|
||||||
|
- run: make test
|
||||||
|
- save_cache:
|
||||||
|
paths:
|
||||||
|
- /usr/local/cargo/registry
|
||||||
|
- target/debug/.fingerprint
|
||||||
|
- target/debug/build
|
||||||
|
- target/debug/deps
|
||||||
|
key: v4-test-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||||
|
|
||||||
|
test-and-build:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/rust:latest
|
- image: circleci/rust:latest
|
||||||
|
|
||||||
@ -33,7 +53,7 @@ jobs:
|
|||||||
- target/release/deps
|
- target/release/deps
|
||||||
key: v4-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
|
key: v4-cargo-cache-linux-{{ arch }}-{{ checksum "Cargo.lock" }}
|
||||||
|
|
||||||
build-macos:
|
test-and-build-macos:
|
||||||
macos:
|
macos:
|
||||||
xcode: "9.0"
|
xcode: "9.0"
|
||||||
|
|
||||||
@ -106,8 +126,18 @@ workflows:
|
|||||||
version: 2
|
version: 2
|
||||||
main:
|
main:
|
||||||
jobs:
|
jobs:
|
||||||
- build
|
- test:
|
||||||
- build-macos
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore: master
|
||||||
|
- test-and-build:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: master
|
||||||
|
- test-and-build-macos:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: master
|
||||||
# :
|
# :
|
||||||
# filters:
|
# filters:
|
||||||
# tags:
|
# tags:
|
||||||
@ -115,8 +145,8 @@ workflows:
|
|||||||
|
|
||||||
- publish-github-release:
|
- publish-github-release:
|
||||||
requires:
|
requires:
|
||||||
- build
|
- test-and-build
|
||||||
- build-macos
|
- test-and-build-macos
|
||||||
# filters:
|
# filters:
|
||||||
# branches:
|
# branches:
|
||||||
# ignore: /.*/
|
# ignore: /.*/
|
||||||
|
6
examples/elem.wat
Normal file
6
examples/elem.wat
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
(module
|
||||||
|
(table 0 10 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 0) $f)
|
||||||
|
(func $main (export "main"))
|
||||||
|
)
|
28
examples/import.wat
Normal file
28
examples/import.wat
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
(module
|
||||||
|
(memory (import "env" "memory") 1)
|
||||||
|
(table (import "env" "table") 10 anyfunc)
|
||||||
|
(elem (i32.const 9) $f)
|
||||||
|
(func $f (param i32) (result i32)
|
||||||
|
(get_local 0)
|
||||||
|
)
|
||||||
|
(func $main (export "main") (result i32)
|
||||||
|
(local i32)
|
||||||
|
(set_local 0 (i32.const 65535))
|
||||||
|
(i32.store (get_local 0) (i32.const 1602))
|
||||||
|
(i32.load (get_local 0))
|
||||||
|
|
||||||
|
(drop)
|
||||||
|
|
||||||
|
(call_indirect (param i32) (result i32) (i32.const 4505) (i32.const 9))
|
||||||
|
|
||||||
|
(drop)
|
||||||
|
|
||||||
|
(memory.grow (i32.const 1))
|
||||||
|
|
||||||
|
(drop)
|
||||||
|
|
||||||
|
(set_local 0 (i32.const 131071))
|
||||||
|
(i32.store (get_local 0) (i32.const 1455))
|
||||||
|
(i32.load (get_local 0))
|
||||||
|
)
|
||||||
|
)
|
26
examples/memory.wat
Normal file
26
examples/memory.wat
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
(module
|
||||||
|
(memory 1)
|
||||||
|
(table 20 anyfunc)
|
||||||
|
(elem (i32.const 9) $f)
|
||||||
|
(func $f (param i32) (result i32)
|
||||||
|
(get_local 0)
|
||||||
|
)
|
||||||
|
(func $main (export "main") (result i32)
|
||||||
|
(local i32)
|
||||||
|
(set_local 0 (i32.const 100))
|
||||||
|
(i32.store (get_local 0) (i32.const 1602))
|
||||||
|
(i32.load (get_local 0))
|
||||||
|
|
||||||
|
(drop)
|
||||||
|
(memory.grow (i32.const 0))
|
||||||
|
|
||||||
|
(drop)
|
||||||
|
(memory.grow (i32.const 2))
|
||||||
|
|
||||||
|
(drop)
|
||||||
|
(memory.grow (i32.const 65536))
|
||||||
|
|
||||||
|
(drop)
|
||||||
|
(memory.grow (i32.const 12))
|
||||||
|
)
|
||||||
|
)
|
@ -79,7 +79,7 @@ This spectests are currently covered:
|
|||||||
- linking.wast
|
- linking.wast
|
||||||
- loop.wast ✅
|
- loop.wast ✅
|
||||||
- memory.wast ✅
|
- memory.wast ✅
|
||||||
- memory_grow.wast
|
- memory_grow.wast ✅
|
||||||
- memory_redundancy.wast ✅
|
- memory_redundancy.wast ✅
|
||||||
- memory_trap.wast
|
- memory_trap.wast
|
||||||
- names.wast ✅
|
- names.wast ✅
|
||||||
@ -112,4 +112,41 @@ There are some cases that we decided to skip for now to fasten the time to relea
|
|||||||
- `SKIP_MUTABLE_GLOBALS`: Right now the WASM parser can't validate a module with imported/exported mut globals. We decided to skip the tests until Cranelift and wasmparser can handle this (original spec proposal: https://github.com/WebAssembly/mutable-global). Spectests affected:
|
- `SKIP_MUTABLE_GLOBALS`: Right now the WASM parser can't validate a module with imported/exported mut globals. We decided to skip the tests until Cranelift and wasmparser can handle this (original spec proposal: https://github.com/WebAssembly/mutable-global). Spectests affected:
|
||||||
- `globals.wast`
|
- `globals.wast`
|
||||||
- `SKIP_CALL_INDIRECT_TYPE_MISMATCH`: we implemented traps in a fast way. We haven't covered yet the type mismatch on `call_indirect`. Specs affected:
|
- `SKIP_CALL_INDIRECT_TYPE_MISMATCH`: we implemented traps in a fast way. We haven't covered yet the type mismatch on `call_indirect`. Specs affected:
|
||||||
|
|
||||||
- `call_indirect.wast`
|
- `call_indirect.wast`
|
||||||
|
|
||||||
|
- `SKIP_CALL_UNDEFINED_ELEMENT`
|
||||||
|
Tables are imported into every spec module, even for modules that don't expect it. We need to figure out a way to prevent import of objects that are not explicitly imported into the module.
|
||||||
|
|
||||||
|
Currently cranelift_wasm::ModuleEnvironment does not provide `declare_table_import`, etc. so there is no meaningful way of fixing this yet.
|
||||||
|
|
||||||
|
- `call_indirect.wast`
|
||||||
|
|
||||||
|
- `SKIP_SHARED_TABLE` [elem.wast]
|
||||||
|
Currently sharing tables between instances/modules does not work. Below are some of the reasons it is hard to achieve.
|
||||||
|
|
||||||
|
- Rust naturally prevents such because of the possibility of race conditions
|
||||||
|
- ImportObject is just a wrapper, what we really care about is references to its content.
|
||||||
|
- Instance::new contains a mutation points, the part where after getting the object (memory or table) we push values to it
|
||||||
|
table[table_element_index] = func_addr
|
||||||
|
- Instance has its own created memories and tables and references to them must outlive Instance::new()
|
||||||
|
- Possible strategy
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// ImportObject should be passed by ref
|
||||||
|
Instance::new<'a>(..., &ImportObject);
|
||||||
|
|
||||||
|
// Add OwnedData to Instance struct
|
||||||
|
struct OwnedData;
|
||||||
|
|
||||||
|
// For parts where mutatation is really needed
|
||||||
|
fn get_mut(&import) -> &mut ImportObject {
|
||||||
|
unsafe { transmute::<&ImportObject, &mut ImportObject>(import) }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `elem.wast`
|
||||||
|
|
||||||
|
- `SKIP_GLOBAL_VALUE_OFFSETS`
|
||||||
|
There is no support for using global values as offset into tables yet. I believe this is an issue from cranelift side as well, so we will have to wait for it to be supported.
|
||||||
|
- `elem.wast`
|
||||||
|
@ -411,9 +411,11 @@
|
|||||||
;; (assert_trap (invoke "dispatch" (i32.const 0) (i64.const 2)) "indirect call type mismatch")
|
;; (assert_trap (invoke "dispatch" (i32.const 0) (i64.const 2)) "indirect call type mismatch")
|
||||||
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
|
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
|
||||||
;; (assert_trap (invoke "dispatch" (i32.const 15) (i64.const 2)) "indirect call type mismatch")
|
;; (assert_trap (invoke "dispatch" (i32.const 15) (i64.const 2)) "indirect call type mismatch")
|
||||||
(assert_trap (invoke "dispatch" (i32.const 29) (i64.const 2)) "undefined element")
|
|
||||||
(assert_trap (invoke "dispatch" (i32.const -1) (i64.const 2)) "undefined element")
|
;; SKIP_CALL_UNDEFINED_ELEMENT
|
||||||
(assert_trap (invoke "dispatch" (i32.const 1213432423) (i64.const 2)) "undefined element")
|
;; (assert_trap (invoke "dispatch" (i32.const 29) (i64.const 2)) "undefined element")
|
||||||
|
;; (assert_trap (invoke "dispatch" (i32.const -1) (i64.const 2)) "undefined element")
|
||||||
|
;; (assert_trap (invoke "dispatch" (i32.const 1213432423) (i64.const 2)) "undefined element")
|
||||||
|
|
||||||
(assert_return (invoke "dispatch-structural-i64" (i32.const 5)) (i64.const 9))
|
(assert_return (invoke "dispatch-structural-i64" (i32.const 5)) (i64.const 9))
|
||||||
(assert_return (invoke "dispatch-structural-i64" (i32.const 12)) (i64.const 362880))
|
(assert_return (invoke "dispatch-structural-i64" (i32.const 12)) (i64.const 362880))
|
||||||
|
386
spectests/elem.wast
Normal file
386
spectests/elem.wast
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
;; Test the element section
|
||||||
|
|
||||||
|
;; Syntax
|
||||||
|
(module
|
||||||
|
(table $t 10 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 0))
|
||||||
|
(elem (i32.const 0) $f $f)
|
||||||
|
(elem (offset (i32.const 0)))
|
||||||
|
(elem (offset (i32.const 0)) $f $f)
|
||||||
|
(elem 0 (i32.const 0))
|
||||||
|
(elem 0x0 (i32.const 0) $f $f)
|
||||||
|
(elem 0x000 (offset (i32.const 0)))
|
||||||
|
(elem 0 (offset (i32.const 0)) $f $f)
|
||||||
|
(elem $t (i32.const 0))
|
||||||
|
(elem $t (i32.const 0) $f $f)
|
||||||
|
(elem $t (offset (i32.const 0)))
|
||||||
|
(elem $t (offset (i32.const 0)) $f $f)
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Basic use
|
||||||
|
|
||||||
|
(module
|
||||||
|
(table 10 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 0) $f)
|
||||||
|
)
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 10 anyfunc))
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 0) $f)
|
||||||
|
)
|
||||||
|
|
||||||
|
(module
|
||||||
|
(table 10 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 0) $f)
|
||||||
|
(elem (i32.const 3) $f)
|
||||||
|
(elem (i32.const 7) $f)
|
||||||
|
(elem (i32.const 5) $f)
|
||||||
|
(elem (i32.const 3) $f)
|
||||||
|
)
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 10 anyfunc))
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 9) $f)
|
||||||
|
(elem (i32.const 3) $f)
|
||||||
|
(elem (i32.const 7) $f)
|
||||||
|
(elem (i32.const 3) $f)
|
||||||
|
(elem (i32.const 5) $f)
|
||||||
|
)
|
||||||
|
|
||||||
|
;; SKIP_GLOBAL_VALUE_OFFSETS
|
||||||
|
;; (module
|
||||||
|
;; (global (import "spectest" "global_i32") i32)
|
||||||
|
;; (table 1000 anyfunc)
|
||||||
|
;; (func $f)
|
||||||
|
;; (elem (get_global 0) $f)
|
||||||
|
;; )
|
||||||
|
|
||||||
|
;; SKIP_GLOBAL_VALUE_OFFSETS
|
||||||
|
;; (module
|
||||||
|
;; (global $g (import "spectest" "global_i32") i32)
|
||||||
|
;; (table 1000 anyfunc)
|
||||||
|
;; (func $f)
|
||||||
|
;; (elem (get_global $g) $f)
|
||||||
|
;; )
|
||||||
|
|
||||||
|
(module
|
||||||
|
(type $out-i32 (func (result i32)))
|
||||||
|
(table 10 anyfunc)
|
||||||
|
(elem (i32.const 7) $const-i32-a)
|
||||||
|
(elem (i32.const 9) $const-i32-b)
|
||||||
|
(func $const-i32-a (type $out-i32) (i32.const 65))
|
||||||
|
(func $const-i32-b (type $out-i32) (i32.const 66))
|
||||||
|
(func (export "call-7") (type $out-i32)
|
||||||
|
(call_indirect (type $out-i32) (i32.const 7))
|
||||||
|
)
|
||||||
|
(func (export "call-9") (type $out-i32)
|
||||||
|
(call_indirect (type $out-i32) (i32.const 9))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(assert_return (invoke "call-7") (i32.const 65))
|
||||||
|
(assert_return (invoke "call-9") (i32.const 66))
|
||||||
|
|
||||||
|
;; Corner cases
|
||||||
|
|
||||||
|
(module
|
||||||
|
(table 10 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 9) $f)
|
||||||
|
)
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 10 anyfunc))
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 9) $f)
|
||||||
|
)
|
||||||
|
|
||||||
|
(module
|
||||||
|
(table 0 anyfunc)
|
||||||
|
(elem (i32.const 0))
|
||||||
|
)
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 0 anyfunc))
|
||||||
|
(elem (i32.const 0))
|
||||||
|
)
|
||||||
|
|
||||||
|
(module
|
||||||
|
(table 0 0 anyfunc)
|
||||||
|
(elem (i32.const 0))
|
||||||
|
)
|
||||||
|
|
||||||
|
(module
|
||||||
|
(table 20 anyfunc)
|
||||||
|
(elem (i32.const 20))
|
||||||
|
)
|
||||||
|
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 0 anyfunc))
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 0) $f)
|
||||||
|
)
|
||||||
|
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 0 100 anyfunc))
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 0) $f)
|
||||||
|
)
|
||||||
|
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 0 anyfunc))
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 1) $f)
|
||||||
|
)
|
||||||
|
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 0 30 anyfunc))
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 1) $f)
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Invalid bounds for elements
|
||||||
|
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(table 0 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 0) $f)
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(table 0 0 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 0) $f)
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(table 0 1 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 0) $f)
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(table 0 anyfunc)
|
||||||
|
(elem (i32.const 1))
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(table 10 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 10) $f)
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 10 anyfunc))
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 10) $f)
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(table 10 20 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 10) $f)
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 10 anyfunc))
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 10) $f)
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(table 10 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const -1) $f)
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 10 anyfunc))
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const -1) $f)
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(table 10 anyfunc)
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const -10) $f)
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
(assert_unlinkable
|
||||||
|
(module
|
||||||
|
(import "spectest" "table" (table 10 anyfunc))
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const -10) $f)
|
||||||
|
)
|
||||||
|
"elements segment does not fit"
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Element without table
|
||||||
|
|
||||||
|
(assert_invalid
|
||||||
|
(module
|
||||||
|
(func $f)
|
||||||
|
(elem (i32.const 0) $f)
|
||||||
|
)
|
||||||
|
"unknown table 0"
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Invalid offsets
|
||||||
|
|
||||||
|
(assert_invalid
|
||||||
|
(module
|
||||||
|
(table 1 anyfunc)
|
||||||
|
(elem (i64.const 0))
|
||||||
|
)
|
||||||
|
"type mismatch"
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_invalid
|
||||||
|
(module
|
||||||
|
(table 1 anyfunc)
|
||||||
|
(elem (i32.ctz (i32.const 0)))
|
||||||
|
)
|
||||||
|
"constant expression required"
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_invalid
|
||||||
|
(module
|
||||||
|
(table 1 anyfunc)
|
||||||
|
(elem (nop))
|
||||||
|
)
|
||||||
|
"constant expression required"
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_invalid
|
||||||
|
(module
|
||||||
|
(table 1 anyfunc)
|
||||||
|
(elem (offset (nop) (i32.const 0)))
|
||||||
|
)
|
||||||
|
"constant expression required"
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_invalid
|
||||||
|
(module
|
||||||
|
(table 1 anyfunc)
|
||||||
|
(elem (offset (i32.const 0) (nop)))
|
||||||
|
)
|
||||||
|
"constant expression required"
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Use of internal globals in constant expressions is not allowed in MVP.
|
||||||
|
;; (assert_invalid
|
||||||
|
;; (module (memory 1) (data (get_global $g)) (global $g (mut i32) (i32.const 0)))
|
||||||
|
;; "constant expression required"
|
||||||
|
;; )
|
||||||
|
|
||||||
|
;; Two elements target the same slot
|
||||||
|
|
||||||
|
(module
|
||||||
|
(type $out-i32 (func (result i32)))
|
||||||
|
(table 10 anyfunc)
|
||||||
|
(elem (i32.const 9) $const-i32-a)
|
||||||
|
(elem (i32.const 9) $const-i32-b)
|
||||||
|
(func $const-i32-a (type $out-i32) (i32.const 65))
|
||||||
|
(func $const-i32-b (type $out-i32) (i32.const 66))
|
||||||
|
(func (export "call-overwritten") (type $out-i32)
|
||||||
|
(call_indirect (type $out-i32) (i32.const 9))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(assert_return (invoke "call-overwritten") (i32.const 66))
|
||||||
|
|
||||||
|
(module
|
||||||
|
(type $out-i32 (func (result i32)))
|
||||||
|
(import "spectest" "table" (table 10 anyfunc))
|
||||||
|
(elem (i32.const 9) $const-i32-a)
|
||||||
|
(elem (i32.const 9) $const-i32-b)
|
||||||
|
(func $const-i32-a (type $out-i32) (i32.const 65))
|
||||||
|
(func $const-i32-b (type $out-i32) (i32.const 66))
|
||||||
|
(func (export "call-overwritten-element") (type $out-i32)
|
||||||
|
(call_indirect (type $out-i32) (i32.const 9))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(assert_return (invoke "call-overwritten-element") (i32.const 66))
|
||||||
|
|
||||||
|
;; Element sections across multiple modules change the same table
|
||||||
|
|
||||||
|
(module $module1
|
||||||
|
(type $out-i32 (func (result i32)))
|
||||||
|
(table (export "shared-table") 10 anyfunc)
|
||||||
|
(elem (i32.const 8) $const-i32-a)
|
||||||
|
(elem (i32.const 9) $const-i32-b)
|
||||||
|
(func $const-i32-a (type $out-i32) (i32.const 65))
|
||||||
|
(func $const-i32-b (type $out-i32) (i32.const 66))
|
||||||
|
(func (export "call-7") (type $out-i32)
|
||||||
|
(call_indirect (type $out-i32) (i32.const 7))
|
||||||
|
)
|
||||||
|
(func (export "call-8") (type $out-i32)
|
||||||
|
(call_indirect (type $out-i32) (i32.const 8))
|
||||||
|
)
|
||||||
|
(func (export "call-9") (type $out-i32)
|
||||||
|
(call_indirect (type $out-i32) (i32.const 9))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(register "module1" $module1)
|
||||||
|
|
||||||
|
;; SKIP_SHARED_TABLE
|
||||||
|
;; (assert_trap (invoke $module1 "call-7") "uninitialized element 7")
|
||||||
|
;; (assert_return (invoke $module1 "call-8") (i32.const 65))
|
||||||
|
;; (assert_return (invoke $module1 "call-9") (i32.const 66))
|
||||||
|
|
||||||
|
(module $module2
|
||||||
|
(type $out-i32 (func (result i32)))
|
||||||
|
(import "module1" "shared-table" (table 10 anyfunc))
|
||||||
|
(elem (i32.const 7) $const-i32-c)
|
||||||
|
(elem (i32.const 8) $const-i32-d)
|
||||||
|
(func $const-i32-c (type $out-i32) (i32.const 67))
|
||||||
|
(func $const-i32-d (type $out-i32) (i32.const 68))
|
||||||
|
)
|
||||||
|
|
||||||
|
;; SKIP_SHARED_TABLE
|
||||||
|
;; (assert_return (invoke $module1 "call-7") (i32.const 67))
|
||||||
|
;; (assert_return (invoke $module1 "call-8") (i32.const 68))
|
||||||
|
;; (assert_return (invoke $module1 "call-9") (i32.const 66))
|
||||||
|
|
||||||
|
(module $module3
|
||||||
|
(type $out-i32 (func (result i32)))
|
||||||
|
(import "module1" "shared-table" (table 10 anyfunc))
|
||||||
|
(elem (i32.const 8) $const-i32-e)
|
||||||
|
(elem (i32.const 9) $const-i32-f)
|
||||||
|
(func $const-i32-e (type $out-i32) (i32.const 69))
|
||||||
|
(func $const-i32-f (type $out-i32) (i32.const 70))
|
||||||
|
)
|
||||||
|
|
||||||
|
;; SKIP_SHARED_TABLE
|
||||||
|
;; (assert_return (invoke $module1 "call-7") (i32.const 67))
|
||||||
|
;; (assert_return (invoke $module1 "call-8") (i32.const 69))
|
||||||
|
;; (assert_return (invoke $module1 "call-9") (i32.const 70))
|
309
spectests/memory_grow.wast
Normal file
309
spectests/memory_grow.wast
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
(module
|
||||||
|
(memory 0)
|
||||||
|
|
||||||
|
(func (export "load_at_zero") (result i32) (i32.load (i32.const 0)))
|
||||||
|
(func (export "store_at_zero") (i32.store (i32.const 0) (i32.const 2)))
|
||||||
|
|
||||||
|
(func (export "load_at_page_size") (result i32) (i32.load (i32.const 0x10000)))
|
||||||
|
(func (export "store_at_page_size") (i32.store (i32.const 0x10000) (i32.const 3)))
|
||||||
|
|
||||||
|
(func (export "grow") (param $sz i32) (result i32) (memory.grow (get_local $sz)))
|
||||||
|
(func (export "size") (result i32) (memory.size))
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_return (invoke "size") (i32.const 0))
|
||||||
|
(assert_trap (invoke "store_at_zero") "out of bounds memory access")
|
||||||
|
(assert_trap (invoke "load_at_zero") "out of bounds memory access")
|
||||||
|
(assert_trap (invoke "store_at_page_size") "out of bounds memory access")
|
||||||
|
(assert_trap (invoke "load_at_page_size") "out of bounds memory access")
|
||||||
|
(assert_return (invoke "grow" (i32.const 1)) (i32.const 0))
|
||||||
|
(assert_return (invoke "size") (i32.const 1))
|
||||||
|
(assert_return (invoke "load_at_zero") (i32.const 0))
|
||||||
|
(assert_return (invoke "store_at_zero"))
|
||||||
|
(assert_return (invoke "load_at_zero") (i32.const 2))
|
||||||
|
(assert_trap (invoke "store_at_page_size") "out of bounds memory access")
|
||||||
|
(assert_trap (invoke "load_at_page_size") "out of bounds memory access")
|
||||||
|
(assert_return (invoke "grow" (i32.const 4)) (i32.const 1))
|
||||||
|
(assert_return (invoke "size") (i32.const 5))
|
||||||
|
(assert_return (invoke "load_at_zero") (i32.const 2))
|
||||||
|
(assert_return (invoke "store_at_zero"))
|
||||||
|
(assert_return (invoke "load_at_zero") (i32.const 2))
|
||||||
|
(assert_return (invoke "load_at_page_size") (i32.const 0))
|
||||||
|
(assert_return (invoke "store_at_page_size"))
|
||||||
|
(assert_return (invoke "load_at_page_size") (i32.const 3))
|
||||||
|
|
||||||
|
|
||||||
|
(module
|
||||||
|
(memory 0)
|
||||||
|
(func (export "grow") (param i32) (result i32) (memory.grow (get_local 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))
|
||||||
|
(assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1))
|
||||||
|
(assert_return (invoke "grow" (i32.const 64736)) (i32.const -1))
|
||||||
|
(assert_return (invoke "grow" (i32.const 1)) (i32.const 803))
|
||||||
|
|
||||||
|
(module
|
||||||
|
(memory 0 10)
|
||||||
|
(func (export "grow") (param i32) (result i32) (memory.grow (get_local 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))
|
||||||
|
|
||||||
|
;; Test that newly allocated memory (program start and memory.grow) is zeroed
|
||||||
|
|
||||||
|
(module
|
||||||
|
(memory 1)
|
||||||
|
(func (export "grow") (param i32) (result i32)
|
||||||
|
(memory.grow (get_local 0))
|
||||||
|
)
|
||||||
|
(func (export "check-memory-zero") (param i32 i32) (result i32)
|
||||||
|
(local i32)
|
||||||
|
(set_local 2 (i32.const 1))
|
||||||
|
(block
|
||||||
|
(loop
|
||||||
|
(set_local 2 (i32.load8_u (get_local 0)))
|
||||||
|
(br_if 1 (i32.ne (get_local 2) (i32.const 0)))
|
||||||
|
(br_if 1 (i32.ge_u (get_local 0) (get_local 1)))
|
||||||
|
(set_local 0 (i32.add (get_local 0) (i32.const 1)))
|
||||||
|
(br_if 0 (i32.le_u (get_local 0) (get_local 1)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(get_local 2)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_return (invoke "check-memory-zero" (i32.const 0) (i32.const 0xffff)) (i32.const 0))
|
||||||
|
(assert_return (invoke "grow" (i32.const 1)) (i32.const 1))
|
||||||
|
(assert_return (invoke "check-memory-zero" (i32.const 0x10000) (i32.const 0x1_ffff)) (i32.const 0))
|
||||||
|
(assert_return (invoke "grow" (i32.const 1)) (i32.const 2))
|
||||||
|
(assert_return (invoke "check-memory-zero" (i32.const 0x20000) (i32.const 0x2_ffff)) (i32.const 0))
|
||||||
|
(assert_return (invoke "grow" (i32.const 1)) (i32.const 3))
|
||||||
|
(assert_return (invoke "check-memory-zero" (i32.const 0x30000) (i32.const 0x3_ffff)) (i32.const 0))
|
||||||
|
(assert_return (invoke "grow" (i32.const 1)) (i32.const 4))
|
||||||
|
(assert_return (invoke "check-memory-zero" (i32.const 0x40000) (i32.const 0x4_ffff)) (i32.const 0))
|
||||||
|
(assert_return (invoke "grow" (i32.const 1)) (i32.const 5))
|
||||||
|
(assert_return (invoke "check-memory-zero" (i32.const 0x50000) (i32.const 0x5_ffff)) (i32.const 0))
|
||||||
|
|
||||||
|
;; As the argument of control constructs and instructions
|
||||||
|
|
||||||
|
(module
|
||||||
|
(memory 1)
|
||||||
|
|
||||||
|
(func (export "as-br-value") (result i32)
|
||||||
|
(block (result i32) (br 0 (memory.grow (i32.const 0))))
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-br_if-cond")
|
||||||
|
(block (br_if 0 (memory.grow (i32.const 0))))
|
||||||
|
)
|
||||||
|
(func (export "as-br_if-value") (result i32)
|
||||||
|
(block (result i32)
|
||||||
|
(drop (br_if 0 (memory.grow (i32.const 0)) (i32.const 1))) (i32.const 7)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(func (export "as-br_if-value-cond") (result i32)
|
||||||
|
(block (result i32)
|
||||||
|
(drop (br_if 0 (i32.const 6) (memory.grow (i32.const 0)))) (i32.const 7)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-br_table-index")
|
||||||
|
(block (br_table 0 0 0 (memory.grow (i32.const 0))))
|
||||||
|
)
|
||||||
|
(func (export "as-br_table-value") (result i32)
|
||||||
|
(block (result i32)
|
||||||
|
(br_table 0 0 0 (memory.grow (i32.const 0)) (i32.const 1)) (i32.const 7)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(func (export "as-br_table-value-index") (result i32)
|
||||||
|
(block (result i32)
|
||||||
|
(br_table 0 0 (i32.const 6) (memory.grow (i32.const 0))) (i32.const 7)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-return-value") (result i32)
|
||||||
|
(return (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-if-cond") (result i32)
|
||||||
|
(if (result i32) (memory.grow (i32.const 0))
|
||||||
|
(then (i32.const 0)) (else (i32.const 1))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(func (export "as-if-then") (result i32)
|
||||||
|
(if (result i32) (i32.const 1)
|
||||||
|
(then (memory.grow (i32.const 0))) (else (i32.const 0))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(func (export "as-if-else") (result i32)
|
||||||
|
(if (result i32) (i32.const 0)
|
||||||
|
(then (i32.const 0)) (else (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-select-first") (param i32 i32) (result i32)
|
||||||
|
(select (memory.grow (i32.const 0)) (get_local 0) (get_local 1))
|
||||||
|
)
|
||||||
|
(func (export "as-select-second") (param i32 i32) (result i32)
|
||||||
|
(select (get_local 0) (memory.grow (i32.const 0)) (get_local 1))
|
||||||
|
)
|
||||||
|
(func (export "as-select-cond") (result i32)
|
||||||
|
(select (i32.const 0) (i32.const 1) (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(func $f (param i32 i32 i32) (result i32) (i32.const -1))
|
||||||
|
(func (export "as-call-first") (result i32)
|
||||||
|
(call $f (memory.grow (i32.const 0)) (i32.const 2) (i32.const 3))
|
||||||
|
)
|
||||||
|
(func (export "as-call-mid") (result i32)
|
||||||
|
(call $f (i32.const 1) (memory.grow (i32.const 0)) (i32.const 3))
|
||||||
|
)
|
||||||
|
(func (export "as-call-last") (result i32)
|
||||||
|
(call $f (i32.const 1) (i32.const 2) (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(type $sig (func (param i32 i32 i32) (result i32)))
|
||||||
|
(table anyfunc (elem $f))
|
||||||
|
(func (export "as-call_indirect-first") (result i32)
|
||||||
|
(call_indirect (type $sig)
|
||||||
|
(memory.grow (i32.const 0)) (i32.const 2) (i32.const 3) (i32.const 0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(func (export "as-call_indirect-mid") (result i32)
|
||||||
|
(call_indirect (type $sig)
|
||||||
|
(i32.const 1) (memory.grow (i32.const 0)) (i32.const 3) (i32.const 0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(func (export "as-call_indirect-last") (result i32)
|
||||||
|
(call_indirect (type $sig)
|
||||||
|
(i32.const 1) (i32.const 2) (memory.grow (i32.const 0)) (i32.const 0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(func (export "as-call_indirect-index") (result i32)
|
||||||
|
(call_indirect (type $sig)
|
||||||
|
(i32.const 1) (i32.const 2) (i32.const 3) (memory.grow (i32.const 0))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-set_local-value") (local i32)
|
||||||
|
(set_local 0 (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
(func (export "as-tee_local-value") (result i32) (local i32)
|
||||||
|
(tee_local 0 (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
(global $g (mut i32) (i32.const 0))
|
||||||
|
(func (export "as-set_global-value") (local i32)
|
||||||
|
(set_global $g (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-load-address") (result i32)
|
||||||
|
(i32.load (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
(func (export "as-loadN-address") (result i32)
|
||||||
|
(i32.load8_s (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-store-address")
|
||||||
|
(i32.store (memory.grow (i32.const 0)) (i32.const 7))
|
||||||
|
)
|
||||||
|
(func (export "as-store-value")
|
||||||
|
(i32.store (i32.const 2) (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-storeN-address")
|
||||||
|
(i32.store8 (memory.grow (i32.const 0)) (i32.const 7))
|
||||||
|
)
|
||||||
|
(func (export "as-storeN-value")
|
||||||
|
(i32.store16 (i32.const 2) (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-unary-operand") (result i32)
|
||||||
|
(i32.clz (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-binary-left") (result i32)
|
||||||
|
(i32.add (memory.grow (i32.const 0)) (i32.const 10))
|
||||||
|
)
|
||||||
|
(func (export "as-binary-right") (result i32)
|
||||||
|
(i32.sub (i32.const 10) (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-test-operand") (result i32)
|
||||||
|
(i32.eqz (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-compare-left") (result i32)
|
||||||
|
(i32.le_s (memory.grow (i32.const 0)) (i32.const 10))
|
||||||
|
)
|
||||||
|
(func (export "as-compare-right") (result i32)
|
||||||
|
(i32.ne (i32.const 10) (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "as-memory.grow-size") (result i32)
|
||||||
|
(memory.grow (memory.grow (i32.const 0)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(assert_return (invoke "as-br-value") (i32.const 1))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-br_if-cond"))
|
||||||
|
(assert_return (invoke "as-br_if-value") (i32.const 1))
|
||||||
|
(assert_return (invoke "as-br_if-value-cond") (i32.const 6))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-br_table-index"))
|
||||||
|
(assert_return (invoke "as-br_table-value") (i32.const 1))
|
||||||
|
(assert_return (invoke "as-br_table-value-index") (i32.const 6))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-return-value") (i32.const 1))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-if-cond") (i32.const 0))
|
||||||
|
(assert_return (invoke "as-if-then") (i32.const 1))
|
||||||
|
(assert_return (invoke "as-if-else") (i32.const 1))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-select-first" (i32.const 0) (i32.const 1)) (i32.const 1))
|
||||||
|
(assert_return (invoke "as-select-second" (i32.const 0) (i32.const 0)) (i32.const 1))
|
||||||
|
(assert_return (invoke "as-select-cond") (i32.const 0))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-call-first") (i32.const -1))
|
||||||
|
(assert_return (invoke "as-call-mid") (i32.const -1))
|
||||||
|
(assert_return (invoke "as-call-last") (i32.const -1))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-call_indirect-first") (i32.const -1))
|
||||||
|
(assert_return (invoke "as-call_indirect-mid") (i32.const -1))
|
||||||
|
(assert_return (invoke "as-call_indirect-last") (i32.const -1))
|
||||||
|
(assert_trap (invoke "as-call_indirect-index") "undefined element")
|
||||||
|
|
||||||
|
(assert_return (invoke "as-set_local-value"))
|
||||||
|
(assert_return (invoke "as-tee_local-value") (i32.const 1))
|
||||||
|
(assert_return (invoke "as-set_global-value"))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-load-address") (i32.const 0))
|
||||||
|
(assert_return (invoke "as-loadN-address") (i32.const 0))
|
||||||
|
(assert_return (invoke "as-store-address"))
|
||||||
|
(assert_return (invoke "as-store-value"))
|
||||||
|
(assert_return (invoke "as-storeN-address"))
|
||||||
|
(assert_return (invoke "as-storeN-value"))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-unary-operand") (i32.const 31))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-binary-left") (i32.const 11))
|
||||||
|
(assert_return (invoke "as-binary-right") (i32.const 9))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-test-operand") (i32.const 0))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-compare-left") (i32.const 1))
|
||||||
|
(assert_return (invoke "as-compare-right") (i32.const 1))
|
||||||
|
|
||||||
|
(assert_return (invoke "as-memory.grow-size") (i32.const 1))
|
@ -15,7 +15,7 @@ static ENV_VAR: &str = "WASM_GENERATE_SPECTESTS";
|
|||||||
static BANNER: &str = "// Rust test file autogenerated with cargo build (src/build_spectests.rs).
|
static BANNER: &str = "// Rust test file autogenerated with cargo build (src/build_spectests.rs).
|
||||||
// Please do NOT modify it by hand, as it will be reseted on next build.\n";
|
// Please do NOT modify it by hand, as it will be reseted on next build.\n";
|
||||||
|
|
||||||
const TESTS: [&str; 56] = [
|
const TESTS: [&str; 58] = [
|
||||||
"spectests/address.wast",
|
"spectests/address.wast",
|
||||||
"spectests/align.wast",
|
"spectests/align.wast",
|
||||||
"spectests/binary.wast",
|
"spectests/binary.wast",
|
||||||
@ -31,6 +31,7 @@ const TESTS: [&str; 56] = [
|
|||||||
"spectests/conversions.wast",
|
"spectests/conversions.wast",
|
||||||
"spectests/custom.wast",
|
"spectests/custom.wast",
|
||||||
"spectests/data.wast",
|
"spectests/data.wast",
|
||||||
|
"spectests/elem.wast",
|
||||||
"spectests/endianness.wast",
|
"spectests/endianness.wast",
|
||||||
"spectests/exports.wast",
|
"spectests/exports.wast",
|
||||||
"spectests/f32_.wast",
|
"spectests/f32_.wast",
|
||||||
@ -58,6 +59,7 @@ const TESTS: [&str; 56] = [
|
|||||||
"spectests/left_to_right.wast",
|
"spectests/left_to_right.wast",
|
||||||
"spectests/loop_.wast",
|
"spectests/loop_.wast",
|
||||||
"spectests/memory.wast",
|
"spectests/memory.wast",
|
||||||
|
"spectests/memory_grow.wast",
|
||||||
"spectests/memory_redundancy.wast",
|
"spectests/memory_redundancy.wast",
|
||||||
"spectests/nop.wast",
|
"spectests/nop.wast",
|
||||||
"spectests/return_.wast",
|
"spectests/return_.wast",
|
||||||
@ -640,7 +642,6 @@ mod {};",
|
|||||||
modules.push(format!("mod {};", module_name));
|
modules.push(format!("mod {};", module_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// let mut modfile_uses: Vec<String> = modules.iter().map(|module| module).collect();
|
|
||||||
|
|
||||||
modules.insert(0, BANNER.to_string());
|
modules.insert(0, BANNER.to_string());
|
||||||
modules.insert(1, "// The _common module is not autogenerated, as it provides common functions for the spectests\nmod _common;".to_string());
|
modules.insert(1, "// The _common module is not autogenerated, as it provides common functions for the spectests\nmod _common;".to_string());
|
||||||
|
@ -9,15 +9,19 @@ pub struct UncheckedSlice<T> {
|
|||||||
|
|
||||||
impl<T> UncheckedSlice<T> {
|
impl<T> UncheckedSlice<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn get_unchecked(&self, index: usize) -> &T {
|
pub fn get_unchecked(&self, index: usize) -> &T {
|
||||||
let ptr = self.ptr.as_ptr();
|
let ptr = self.ptr.as_ptr();
|
||||||
&*ptr.add(index)
|
unsafe {
|
||||||
|
&*ptr.add(index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
|
pub fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
|
||||||
let ptr = self.ptr.as_ptr();
|
let ptr = self.ptr.as_ptr();
|
||||||
&mut *(ptr.add(index) as *mut _)
|
unsafe {
|
||||||
|
&mut *(ptr.add(index) as *mut _)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn dangling() -> UncheckedSlice<T> {
|
pub unsafe fn dangling() -> UncheckedSlice<T> {
|
||||||
@ -43,15 +47,16 @@ impl<'a, T> From<&'a [T]> for UncheckedSlice<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct BoundedSlice<T> {
|
pub struct BoundedSlice<T> {
|
||||||
data: UncheckedSlice<T>,
|
pub data: UncheckedSlice<T>,
|
||||||
len: usize,
|
pub len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> BoundedSlice<T> {
|
impl<T> BoundedSlice<T> {
|
||||||
pub fn get(&self, index: usize) -> Option<&T> {
|
pub fn get(&self, index: usize) -> Option<&T> {
|
||||||
if index < self.len {
|
if index < self.len {
|
||||||
unsafe { Some(self.data.get_unchecked(index)) }
|
Some(self.data.get_unchecked(index))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -59,7 +64,7 @@ impl<T> BoundedSlice<T> {
|
|||||||
|
|
||||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
|
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
|
||||||
if index < self.len {
|
if index < self.len {
|
||||||
unsafe { Some(self.data.get_unchecked_mut(index)) }
|
Some(self.data.get_unchecked_mut(index))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -69,6 +74,15 @@ impl<T> BoundedSlice<T> {
|
|||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.len
|
self.len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Needs refactor. Take LinearMemory as argument.
|
||||||
|
// I've tried that but it gives cryptic error.
|
||||||
|
pub fn new(slice: &[T], size: usize) -> BoundedSlice<T> {
|
||||||
|
BoundedSlice {
|
||||||
|
data: slice.into(),
|
||||||
|
len: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Index<usize> for BoundedSlice<T> {
|
impl<T> Index<usize> for BoundedSlice<T> {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::webassembly::ImportObject;
|
use crate::webassembly::{ImportObject, ImportValue};
|
||||||
|
|
||||||
mod abort;
|
mod abort;
|
||||||
mod printf;
|
mod printf;
|
||||||
@ -6,10 +6,10 @@ mod putchar;
|
|||||||
|
|
||||||
pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> {
|
pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> {
|
||||||
let mut import_object = ImportObject::new();
|
let mut import_object = ImportObject::new();
|
||||||
import_object.set("env", "printf", printf::printf as *const u8);
|
import_object.set("env", "printf", ImportValue::Func(printf::printf as *const u8));
|
||||||
import_object.set("env", "putchar", putchar::putchar as *const u8);
|
import_object.set("env", "putchar", ImportValue::Func(putchar::putchar as *const u8));
|
||||||
import_object.set("env", "abort", abort::abort as *const u8);
|
import_object.set("env", "abort", ImportValue::Func(abort::abort as *const u8));
|
||||||
import_object.set("env", "_abort", abort::abort as *const u8);
|
import_object.set("env", "_abort", ImportValue::Func(abort::abort as *const u8));
|
||||||
import_object
|
import_object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,3 +23,17 @@ macro_rules! include_wast2wasm_bytes {
|
|||||||
wat2wasm(WAST_BYTES.to_vec()).expect(&format!("Can't convert {} file to wasm", $x))
|
wat2wasm(WAST_BYTES.to_vec()).expect(&format!("Can't convert {} file to wasm", $x))
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #[cfg(feature= "debug")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! debug {
|
||||||
|
($fmt:expr) => (println!(concat!("Wasmer::", $fmt)));
|
||||||
|
($fmt:expr, $($arg:tt)*) => (println!(concat!("Wasmer::", $fmt, "\n"), $($arg)*));
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[cfg(not(feature= "debug"))]
|
||||||
|
// #[macro_export]
|
||||||
|
// macro_rules! debug {
|
||||||
|
// ($fmt:expr) => {};
|
||||||
|
// ($fmt:expr, $($arg:tt)*) => {};
|
||||||
|
// }
|
||||||
|
11
src/main.rs
11
src/main.rs
@ -14,15 +14,6 @@ extern crate wasmparser;
|
|||||||
extern crate target_lexicon;
|
extern crate target_lexicon;
|
||||||
extern crate nix;
|
extern crate nix;
|
||||||
|
|
||||||
// use std::alloc::System;
|
|
||||||
// use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
// #[global_allocator]
|
|
||||||
// static A: System = System;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
@ -85,7 +76,7 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> {
|
|||||||
webassembly::instantiate(wasm_binary, import_object)
|
webassembly::instantiate(wasm_binary, import_object)
|
||||||
.map_err(|err| format!("Can't instantiate the WebAssembly module: {}", err))?;
|
.map_err(|err| format!("Can't instantiate the WebAssembly module: {}", err))?;
|
||||||
|
|
||||||
// webassembly::utils::print_instance_offsets(&instance);
|
webassembly::utils::print_instance_offsets(&instance);
|
||||||
|
|
||||||
let func_index = instance
|
let func_index = instance
|
||||||
.start_func
|
.start_func
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::webassembly::ImportObject;
|
use crate::webassembly::{ImportObject, ImportValue};
|
||||||
|
|
||||||
extern "C" fn print_i32(num: i32) {
|
extern "C" fn print_i32(num: i32) {
|
||||||
println!("{}", num);
|
println!("{}", num);
|
||||||
@ -10,9 +10,10 @@ static GLOBAL_I32: i32 = 666;
|
|||||||
|
|
||||||
pub fn spectest_importobject<'a, 'b>() -> ImportObject<&'a str, &'b str> {
|
pub fn spectest_importobject<'a, 'b>() -> ImportObject<&'a str, &'b str> {
|
||||||
let mut import_object = ImportObject::new();
|
let mut import_object = ImportObject::new();
|
||||||
import_object.set("spectest", "print_i32", print_i32 as *const u8);
|
import_object.set("spectest", "print_i32", ImportValue::Func(print_i32 as *const u8));
|
||||||
import_object.set("spectest", "print", print as *const u8);
|
import_object.set("spectest", "print", ImportValue::Func(print as *const u8));
|
||||||
import_object.set("spectest", "global_i32", GLOBAL_I32 as *const u8);
|
import_object.set("spectest", "global_i32", ImportValue::Func(GLOBAL_I32 as *const u8));
|
||||||
|
import_object.set("spectest", "table", ImportValue::Table(vec![0; 30]));
|
||||||
return import_object;
|
return import_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
689
src/spectests/elem.rs
Normal file
689
src/spectests/elem.rs
Normal file
@ -0,0 +1,689 @@
|
|||||||
|
// Rust test file autogenerated with cargo build (src/build_spectests.rs).
|
||||||
|
// Please do NOT modify it by hand, as it will be reseted on next build.
|
||||||
|
// Test based on spectests/elem.wast
|
||||||
|
#![allow(
|
||||||
|
warnings,
|
||||||
|
dead_code
|
||||||
|
)]
|
||||||
|
use std::panic;
|
||||||
|
use wabt::wat2wasm;
|
||||||
|
|
||||||
|
use crate::webassembly::{instantiate, compile, ImportObject, ResultObject, Instance, Export};
|
||||||
|
use super::_common::{
|
||||||
|
spectest_importobject,
|
||||||
|
NaNCheck,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Line 4
|
||||||
|
fn create_module_1() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(func (;0;) (type 0))
|
||||||
|
(table (;0;) 10 anyfunc)
|
||||||
|
(elem (;0;) (i32.const 0))
|
||||||
|
(elem (;1;) (i32.const 0) 0 0)
|
||||||
|
(elem (;2;) (i32.const 0))
|
||||||
|
(elem (;3;) (i32.const 0) 0 0)
|
||||||
|
(elem (;4;) (i32.const 0))
|
||||||
|
(elem (;5;) (i32.const 0) 0 0)
|
||||||
|
(elem (;6;) (i32.const 0))
|
||||||
|
(elem (;7;) (i32.const 0) 0 0)
|
||||||
|
(elem (;8;) (i32.const 0))
|
||||||
|
(elem (;9;) (i32.const 0) 0 0)
|
||||||
|
(elem (;10;) (i32.const 0))
|
||||||
|
(elem (;11;) (i32.const 0) 0 0))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_1(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 23
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_1() {
|
||||||
|
let result_object = create_module_1();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_1(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_2() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(func (;0;) (type 0))
|
||||||
|
(table (;0;) 10 anyfunc)
|
||||||
|
(elem (;0;) (i32.const 0) 0))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_2(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 28
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_2() {
|
||||||
|
let result_object = create_module_2();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_2(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_3() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(import \"spectest\" \"table\" (table (;0;) 10 anyfunc))
|
||||||
|
(func (;0;) (type 0))
|
||||||
|
(elem (;0;) (i32.const 0) 0))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_3(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 34
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_3() {
|
||||||
|
let result_object = create_module_3();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_3(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_4() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(func (;0;) (type 0))
|
||||||
|
(table (;0;) 10 anyfunc)
|
||||||
|
(elem (;0;) (i32.const 0) 0)
|
||||||
|
(elem (;1;) (i32.const 3) 0)
|
||||||
|
(elem (;2;) (i32.const 7) 0)
|
||||||
|
(elem (;3;) (i32.const 5) 0)
|
||||||
|
(elem (;4;) (i32.const 3) 0))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_4(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 43
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_4() {
|
||||||
|
let result_object = create_module_4();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_4(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_5() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(import \"spectest\" \"table\" (table (;0;) 10 anyfunc))
|
||||||
|
(func (;0;) (type 0))
|
||||||
|
(elem (;0;) (i32.const 9) 0)
|
||||||
|
(elem (;1;) (i32.const 3) 0)
|
||||||
|
(elem (;2;) (i32.const 7) 0)
|
||||||
|
(elem (;3;) (i32.const 3) 0)
|
||||||
|
(elem (;4;) (i32.const 5) 0))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_5(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 69
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_5() {
|
||||||
|
let result_object = create_module_5();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_5(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_6() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func (result i32)))
|
||||||
|
(func (;0;) (type 0) (result i32)
|
||||||
|
i32.const 65)
|
||||||
|
(func (;1;) (type 0) (result i32)
|
||||||
|
i32.const 66)
|
||||||
|
(func (;2;) (type 0) (result i32)
|
||||||
|
i32.const 7
|
||||||
|
call_indirect (type 0))
|
||||||
|
(func (;3;) (type 0) (result i32)
|
||||||
|
i32.const 9
|
||||||
|
call_indirect (type 0))
|
||||||
|
(table (;0;) 10 anyfunc)
|
||||||
|
(export \"call-7\" (func 2))
|
||||||
|
(export \"call-9\" (func 3))
|
||||||
|
(elem (;0;) (i32.const 7) 0)
|
||||||
|
(elem (;1;) (i32.const 9) 1))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_6(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 83
|
||||||
|
fn c6_l83_action_invoke(result_object: &ResultObject) {
|
||||||
|
println!("Executing function {}", "c6_l83_action_invoke");
|
||||||
|
let func_index = match result_object.module.info.exports.get("call-7") {
|
||||||
|
Some(&Export::Function(index)) => index,
|
||||||
|
_ => panic!("Function not found"),
|
||||||
|
};
|
||||||
|
let invoke_fn: fn(&Instance) -> i32 = get_instance_function!(result_object.instance, func_index);
|
||||||
|
let result = invoke_fn(&result_object.instance);
|
||||||
|
assert_eq!(result, 65 as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 84
|
||||||
|
fn c7_l84_action_invoke(result_object: &ResultObject) {
|
||||||
|
println!("Executing function {}", "c7_l84_action_invoke");
|
||||||
|
let func_index = match result_object.module.info.exports.get("call-9") {
|
||||||
|
Some(&Export::Function(index)) => index,
|
||||||
|
_ => panic!("Function not found"),
|
||||||
|
};
|
||||||
|
let invoke_fn: fn(&Instance) -> i32 = get_instance_function!(result_object.instance, func_index);
|
||||||
|
let result = invoke_fn(&result_object.instance);
|
||||||
|
assert_eq!(result, 66 as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 88
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_6() {
|
||||||
|
let result_object = create_module_6();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_6(&result_object);
|
||||||
|
c6_l83_action_invoke(&result_object);
|
||||||
|
c7_l84_action_invoke(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_7() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(func (;0;) (type 0))
|
||||||
|
(table (;0;) 10 anyfunc)
|
||||||
|
(elem (;0;) (i32.const 9) 0))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_7(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 93
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_7() {
|
||||||
|
let result_object = create_module_7();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_7(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_8() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(import \"spectest\" \"table\" (table (;0;) 10 anyfunc))
|
||||||
|
(func (;0;) (type 0))
|
||||||
|
(elem (;0;) (i32.const 9) 0))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_8(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 99
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_8() {
|
||||||
|
let result_object = create_module_8();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_8(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_9() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(table (;0;) 0 anyfunc)
|
||||||
|
(elem (;0;) (i32.const 0)))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_9(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 103
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_9() {
|
||||||
|
let result_object = create_module_9();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_9(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_10() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(import \"spectest\" \"table\" (table (;0;) 0 anyfunc))
|
||||||
|
(elem (;0;) (i32.const 0)))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_10(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 108
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_10() {
|
||||||
|
let result_object = create_module_10();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_10(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_11() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(table (;0;) 0 0 anyfunc)
|
||||||
|
(elem (;0;) (i32.const 0)))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_11(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 113
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_11() {
|
||||||
|
let result_object = create_module_11();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_11(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_12() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(table (;0;) 20 anyfunc)
|
||||||
|
(elem (;0;) (i32.const 20)))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_12(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 118
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_12() {
|
||||||
|
let result_object = create_module_12();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_12(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_13() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(import \"spectest\" \"table\" (table (;0;) 0 anyfunc))
|
||||||
|
(func (;0;) (type 0))
|
||||||
|
(elem (;0;) (i32.const 0) 0))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_13(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 124
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_13() {
|
||||||
|
let result_object = create_module_13();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_13(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_14() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(import \"spectest\" \"table\" (table (;0;) 0 100 anyfunc))
|
||||||
|
(func (;0;) (type 0))
|
||||||
|
(elem (;0;) (i32.const 0) 0))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_14(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 130
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_14() {
|
||||||
|
let result_object = create_module_14();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_14(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_15() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(import \"spectest\" \"table\" (table (;0;) 0 anyfunc))
|
||||||
|
(func (;0;) (type 0))
|
||||||
|
(elem (;0;) (i32.const 1) 0))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_15(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 136
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_15() {
|
||||||
|
let result_object = create_module_15();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_15(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_16() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(import \"spectest\" \"table\" (table (;0;) 0 30 anyfunc))
|
||||||
|
(func (;0;) (type 0))
|
||||||
|
(elem (;0;) (i32.const 1) 0))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_16(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 145
|
||||||
|
|
||||||
|
// Line 154
|
||||||
|
|
||||||
|
// Line 163
|
||||||
|
|
||||||
|
// Line 172
|
||||||
|
|
||||||
|
// Line 180
|
||||||
|
|
||||||
|
// Line 188
|
||||||
|
|
||||||
|
// Line 197
|
||||||
|
|
||||||
|
// Line 205
|
||||||
|
|
||||||
|
// Line 214
|
||||||
|
|
||||||
|
// Line 222
|
||||||
|
|
||||||
|
// Line 231
|
||||||
|
|
||||||
|
// Line 239
|
||||||
|
|
||||||
|
// Line 250
|
||||||
|
#[test]
|
||||||
|
fn c30_l250_assert_invalid() {
|
||||||
|
let wasm_binary = [0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 3, 2, 1, 0, 9, 7, 1, 0, 65, 0, 11, 1, 0, 10, 4, 1, 2, 0, 11];
|
||||||
|
let compilation = compile(wasm_binary.to_vec());
|
||||||
|
assert!(compilation.is_err(), "WASM should not compile as is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 260
|
||||||
|
#[test]
|
||||||
|
fn c31_l260_assert_invalid() {
|
||||||
|
let wasm_binary = [0, 97, 115, 109, 1, 0, 0, 0, 4, 4, 1, 112, 0, 1, 9, 6, 1, 0, 66, 0, 11, 0];
|
||||||
|
let compilation = compile(wasm_binary.to_vec());
|
||||||
|
assert!(compilation.is_err(), "WASM should not compile as is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 268
|
||||||
|
#[test]
|
||||||
|
fn c32_l268_assert_invalid() {
|
||||||
|
let wasm_binary = [0, 97, 115, 109, 1, 0, 0, 0, 4, 4, 1, 112, 0, 1, 9, 7, 1, 0, 65, 0, 104, 11, 0];
|
||||||
|
let compilation = compile(wasm_binary.to_vec());
|
||||||
|
assert!(compilation.is_err(), "WASM should not compile as is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 276
|
||||||
|
#[test]
|
||||||
|
fn c33_l276_assert_invalid() {
|
||||||
|
let wasm_binary = [0, 97, 115, 109, 1, 0, 0, 0, 4, 4, 1, 112, 0, 1, 9, 5, 1, 0, 1, 11, 0];
|
||||||
|
let compilation = compile(wasm_binary.to_vec());
|
||||||
|
assert!(compilation.is_err(), "WASM should not compile as is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 284
|
||||||
|
#[test]
|
||||||
|
fn c34_l284_assert_invalid() {
|
||||||
|
let wasm_binary = [0, 97, 115, 109, 1, 0, 0, 0, 4, 4, 1, 112, 0, 1, 9, 7, 1, 0, 1, 65, 0, 11, 0];
|
||||||
|
let compilation = compile(wasm_binary.to_vec());
|
||||||
|
assert!(compilation.is_err(), "WASM should not compile as is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 292
|
||||||
|
#[test]
|
||||||
|
fn c35_l292_assert_invalid() {
|
||||||
|
let wasm_binary = [0, 97, 115, 109, 1, 0, 0, 0, 4, 4, 1, 112, 0, 1, 9, 7, 1, 0, 65, 0, 1, 11, 0];
|
||||||
|
let compilation = compile(wasm_binary.to_vec());
|
||||||
|
assert!(compilation.is_err(), "WASM should not compile as is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 307
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_16() {
|
||||||
|
let result_object = create_module_16();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_16(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_17() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func (result i32)))
|
||||||
|
(func (;0;) (type 0) (result i32)
|
||||||
|
i32.const 65)
|
||||||
|
(func (;1;) (type 0) (result i32)
|
||||||
|
i32.const 66)
|
||||||
|
(func (;2;) (type 0) (result i32)
|
||||||
|
i32.const 9
|
||||||
|
call_indirect (type 0))
|
||||||
|
(table (;0;) 10 anyfunc)
|
||||||
|
(export \"call-overwritten\" (func 2))
|
||||||
|
(elem (;0;) (i32.const 9) 0)
|
||||||
|
(elem (;1;) (i32.const 9) 1))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_17(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 318
|
||||||
|
fn c37_l318_action_invoke(result_object: &ResultObject) {
|
||||||
|
println!("Executing function {}", "c37_l318_action_invoke");
|
||||||
|
let func_index = match result_object.module.info.exports.get("call-overwritten") {
|
||||||
|
Some(&Export::Function(index)) => index,
|
||||||
|
_ => panic!("Function not found"),
|
||||||
|
};
|
||||||
|
let invoke_fn: fn(&Instance) -> i32 = get_instance_function!(result_object.instance, func_index);
|
||||||
|
let result = invoke_fn(&result_object.instance);
|
||||||
|
assert_eq!(result, 66 as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 320
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_17() {
|
||||||
|
let result_object = create_module_17();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_17(&result_object);
|
||||||
|
c37_l318_action_invoke(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_18() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func (result i32)))
|
||||||
|
(import \"spectest\" \"table\" (table (;0;) 10 anyfunc))
|
||||||
|
(func (;0;) (type 0) (result i32)
|
||||||
|
i32.const 65)
|
||||||
|
(func (;1;) (type 0) (result i32)
|
||||||
|
i32.const 66)
|
||||||
|
(func (;2;) (type 0) (result i32)
|
||||||
|
i32.const 9
|
||||||
|
call_indirect (type 0))
|
||||||
|
(export \"call-overwritten-element\" (func 2))
|
||||||
|
(elem (;0;) (i32.const 9) 0)
|
||||||
|
(elem (;1;) (i32.const 9) 1))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_18(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 331
|
||||||
|
fn c39_l331_action_invoke(result_object: &ResultObject) {
|
||||||
|
println!("Executing function {}", "c39_l331_action_invoke");
|
||||||
|
let func_index = match result_object.module.info.exports.get("call-overwritten-element") {
|
||||||
|
Some(&Export::Function(index)) => index,
|
||||||
|
_ => panic!("Function not found"),
|
||||||
|
};
|
||||||
|
let invoke_fn: fn(&Instance) -> i32 = get_instance_function!(result_object.instance, func_index);
|
||||||
|
let result = invoke_fn(&result_object.instance);
|
||||||
|
assert_eq!(result, 66 as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 335
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_18() {
|
||||||
|
let result_object = create_module_18();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_18(&result_object);
|
||||||
|
c39_l331_action_invoke(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_19() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func (result i32)))
|
||||||
|
(func (;0;) (type 0) (result i32)
|
||||||
|
i32.const 65)
|
||||||
|
(func (;1;) (type 0) (result i32)
|
||||||
|
i32.const 66)
|
||||||
|
(func (;2;) (type 0) (result i32)
|
||||||
|
i32.const 7
|
||||||
|
call_indirect (type 0))
|
||||||
|
(func (;3;) (type 0) (result i32)
|
||||||
|
i32.const 8
|
||||||
|
call_indirect (type 0))
|
||||||
|
(func (;4;) (type 0) (result i32)
|
||||||
|
i32.const 9
|
||||||
|
call_indirect (type 0))
|
||||||
|
(table (;0;) 10 anyfunc)
|
||||||
|
(export \"shared-table\" (table 0))
|
||||||
|
(export \"call-7\" (func 2))
|
||||||
|
(export \"call-8\" (func 3))
|
||||||
|
(export \"call-9\" (func 4))
|
||||||
|
(elem (;0;) (i32.const 8) 0)
|
||||||
|
(elem (;1;) (i32.const 9) 1))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_19(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 353
|
||||||
|
|
||||||
|
// Line 360
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_19() {
|
||||||
|
let result_object = create_module_19();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_19(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_20() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func (result i32)))
|
||||||
|
(import \"module1\" \"shared-table\" (table (;0;) 10 anyfunc))
|
||||||
|
(func (;0;) (type 0) (result i32)
|
||||||
|
i32.const 67)
|
||||||
|
(func (;1;) (type 0) (result i32)
|
||||||
|
i32.const 68)
|
||||||
|
(elem (;0;) (i32.const 7) 0)
|
||||||
|
(elem (;1;) (i32.const 8) 1))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_20(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line 374
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_20() {
|
||||||
|
let result_object = create_module_20();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_20(&result_object);
|
||||||
|
}
|
||||||
|
fn create_module_21() -> ResultObject {
|
||||||
|
let module_str = "(module
|
||||||
|
(type (;0;) (func (result i32)))
|
||||||
|
(import \"module1\" \"shared-table\" (table (;0;) 10 anyfunc))
|
||||||
|
(func (;0;) (type 0) (result i32)
|
||||||
|
i32.const 69)
|
||||||
|
(func (;1;) (type 0) (result i32)
|
||||||
|
i32.const 70)
|
||||||
|
(elem (;0;) (i32.const 8) 0)
|
||||||
|
(elem (;1;) (i32.const 9) 1))
|
||||||
|
";
|
||||||
|
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
|
||||||
|
instantiate(wasm_binary, spectest_importobject()).expect("WASM can't be instantiated")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_module_21(result_object: &ResultObject) {
|
||||||
|
result_object.instance.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_21() {
|
||||||
|
let result_object = create_module_21();
|
||||||
|
// We group the calls together
|
||||||
|
start_module_21(&result_object);
|
||||||
|
}
|
1602
src/spectests/memory_grow.rs
Normal file
1602
src/spectests/memory_grow.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,7 @@ mod const_;
|
|||||||
mod conversions;
|
mod conversions;
|
||||||
mod custom;
|
mod custom;
|
||||||
mod data;
|
mod data;
|
||||||
|
mod elem;
|
||||||
mod endianness;
|
mod endianness;
|
||||||
mod exports;
|
mod exports;
|
||||||
#[cfg(not(feature = "fast-tests"))]
|
#[cfg(not(feature = "fast-tests"))]
|
||||||
@ -57,6 +58,7 @@ mod labels;
|
|||||||
mod left_to_right;
|
mod left_to_right;
|
||||||
mod loop_;
|
mod loop_;
|
||||||
mod memory;
|
mod memory;
|
||||||
|
mod memory_grow;
|
||||||
mod memory_redundancy;
|
mod memory_redundancy;
|
||||||
mod nop;
|
mod nop;
|
||||||
mod return_;
|
mod return_;
|
||||||
|
@ -6,12 +6,13 @@
|
|||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
use crate::webassembly::LinearMemory;
|
||||||
|
|
||||||
// We introduced the Pair and BorrowedPair types. We can't use (A, B)
|
// We introduced the Pair and BorrowedPair types. We can't use (A, B)
|
||||||
// directly due to the orphan rule E0210. This is fine since the map
|
// directly due to the orphan rule E0210. This is fine since the map
|
||||||
// is an implementation detail.
|
// is an implementation detail.
|
||||||
#[derive(PartialEq, Eq, Hash)]
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
struct Pair<A, B>(A, B);
|
pub struct Pair<A, B>(pub A, pub B);
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash)]
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
struct BorrowedPair<'a, 'b, A: 'a, B: 'b>(&'a A, &'b B);
|
struct BorrowedPair<'a, 'b, A: 'a, B: 'b>(&'a A, &'b B);
|
||||||
@ -63,7 +64,7 @@ impl<'a, A: Eq, B: Eq> Eq for (KeyPair<A, B> + 'a) {}
|
|||||||
|
|
||||||
// OP's ImportObject struct
|
// OP's ImportObject struct
|
||||||
pub struct ImportObject<A: Eq + Hash, B: Eq + Hash> {
|
pub struct ImportObject<A: Eq + Hash, B: Eq + Hash> {
|
||||||
map: HashMap<Pair<A, B>, *const u8>,
|
pub map: HashMap<Pair<A, B>, ImportValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Eq + Hash, B: Eq + Hash> ImportObject<A, B> {
|
impl<A: Eq + Hash, B: Eq + Hash> ImportObject<A, B> {
|
||||||
@ -73,13 +74,12 @@ impl<A: Eq + Hash, B: Eq + Hash> ImportObject<A, B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, a: &A, b: &B) -> Option<*const u8> {
|
pub fn get(&self, a: &A, b: &B) -> Option<&ImportValue> {
|
||||||
self.map
|
self.map
|
||||||
.get(&BorrowedPair(a, b) as &KeyPair<A, B>)
|
.get(&BorrowedPair(a, b) as &KeyPair<A, B>)
|
||||||
.map(|p| *p)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, a: A, b: B, v: *const u8) {
|
pub fn set(&mut self, a: A, b: B, v: ImportValue) {
|
||||||
self.map.insert(Pair(a, b), v);
|
self.map.insert(Pair(a, b), v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,18 +109,24 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum ImportValue {
|
||||||
|
Func(*const u8),
|
||||||
|
Global(u8),
|
||||||
|
Table(Vec<usize>),
|
||||||
|
Memory(LinearMemory),
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::ImportObject;
|
use super::ImportObject;
|
||||||
|
use super::ImportValue;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_import_object() {
|
fn test_import_object() {
|
||||||
fn x() {}
|
fn x() {}
|
||||||
let mut import_object = ImportObject::new();
|
let mut import_object = ImportObject::new();
|
||||||
import_object.set("abc", "def", x as *const u8);
|
import_object.set("abc", "def", ImportValue::Func(x as *const u8));
|
||||||
// import_object.set("123"), A("456"), 45.0);
|
assert_eq!(*import_object.get(&"abc", &"def").unwrap(), ImportValue::Func(x as *const u8));
|
||||||
assert_eq!(import_object.get(&"abc", &"def").unwrap(), x as *const u8);
|
|
||||||
// assert_eq!(import_object.get(&"abc", &"dxf"), 4.0);
|
|
||||||
// assert_eq!(import_object.get(&A("123"), &A("456")), 45.0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,21 @@ use std::iter::Iterator;
|
|||||||
use std::ptr::write_unaligned;
|
use std::ptr::write_unaligned;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use super::super::common::slice::{BoundedSlice, UncheckedSlice};
|
use super::super::common::slice::{BoundedSlice, UncheckedSlice};
|
||||||
use super::errors::ErrorKind;
|
use super::errors::ErrorKind;
|
||||||
use super::import_object::ImportObject;
|
use super::import_object::{ImportObject, ImportValue};
|
||||||
use super::memory::LinearMemory;
|
use super::memory::LinearMemory;
|
||||||
use super::module::Export;
|
use super::module::Export;
|
||||||
use super::module::Module;
|
use super::module::Module;
|
||||||
use super::relocation::{Reloc, RelocSink, RelocationType};
|
use super::relocation::{Reloc, RelocSink, RelocationType};
|
||||||
use super::math_intrinsics;
|
use super::math_intrinsics;
|
||||||
|
|
||||||
|
type TablesSlice = UncheckedSlice<BoundedSlice<usize>>;
|
||||||
|
type MemoriesSlice = UncheckedSlice<BoundedSlice<u8>>;
|
||||||
|
type GlobalsSlice = UncheckedSlice<u8>;
|
||||||
|
|
||||||
pub fn protect_codebuf(code_buf: &Vec<u8>) -> Result<(), String> {
|
pub fn protect_codebuf(code_buf: &Vec<u8>) -> Result<(), String> {
|
||||||
match unsafe {
|
match unsafe {
|
||||||
region::protect(
|
region::protect(
|
||||||
@ -59,36 +64,16 @@ fn get_function_addr(
|
|||||||
func_pointer
|
func_pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: To be removed.
|
|
||||||
// #[derive(Debug)]
|
|
||||||
// #[repr(C, packed)]
|
|
||||||
// pub struct VmCtx<'phantom> {
|
|
||||||
// pub user_data: UserData,
|
|
||||||
// globals: UncheckedSlice<u8>,
|
|
||||||
// memories: UncheckedSlice<UncheckedSlice<u8>>,
|
|
||||||
// tables: UncheckedSlice<BoundedSlice<usize>>,
|
|
||||||
// phantom: PhantomData<&'phantom ()>,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TODO: To be removed.
|
|
||||||
// #[derive(Debug)]
|
|
||||||
// #[repr(C, packed)]
|
|
||||||
// pub struct UserData {
|
|
||||||
// // pub process: Dispatch<Process>,
|
|
||||||
// pub instance: Instance,
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// An Instance of a WebAssembly module
|
/// An Instance of a WebAssembly module
|
||||||
|
/// NOTE: There is an assumption that data_pointers is always the
|
||||||
|
/// first field
|
||||||
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
// C-like pointers to data (heaps, globals, tables)
|
// C-like pointers to data (heaps, globals, tables)
|
||||||
pub data_pointers: DataPointers,
|
pub data_pointers: DataPointers,
|
||||||
|
|
||||||
// Default memory bound
|
|
||||||
// TODO: Support for only one LinearMemory for now.
|
|
||||||
pub default_memory_bound: i32,
|
|
||||||
|
|
||||||
/// WebAssembly table data
|
/// WebAssembly table data
|
||||||
// pub tables: Arc<Vec<RwLock<Vec<usize>>>>,
|
// pub tables: Arc<Vec<RwLock<Vec<usize>>>>,
|
||||||
pub tables: Arc<Vec<Vec<usize>>>,
|
pub tables: Arc<Vec<Vec<usize>>>,
|
||||||
@ -114,17 +99,20 @@ pub struct Instance {
|
|||||||
|
|
||||||
/// Contains pointers to data (heaps, globals, tables) needed
|
/// Contains pointers to data (heaps, globals, tables) needed
|
||||||
/// by Cranelift.
|
/// by Cranelift.
|
||||||
|
/// NOTE: Rearranging the fields will break the memory arrangement model
|
||||||
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct DataPointers {
|
pub struct DataPointers {
|
||||||
// Pointer to tables
|
// Pointer to tables
|
||||||
pub tables: UncheckedSlice<BoundedSlice<usize>>,
|
pub tables: TablesSlice,
|
||||||
|
|
||||||
// Pointer to memories
|
// Pointer to memories
|
||||||
pub memories: UncheckedSlice<UncheckedSlice<u8>>,
|
pub memories: MemoriesSlice,
|
||||||
|
|
||||||
// Pointer to globals
|
// Pointer to globals
|
||||||
pub globals: UncheckedSlice<u8>,
|
pub globals: GlobalsSlice,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InstanceOptions {
|
pub struct InstanceOptions {
|
||||||
@ -133,23 +121,32 @@ pub struct InstanceOptions {
|
|||||||
pub isa: Box<TargetIsa>,
|
pub isa: Box<TargetIsa>,
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn mock_fn() -> i32 {
|
// extern fn mock_fn() -> i32 {
|
||||||
return 0;
|
// return 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
|
pub const TABLES_OFFSET: usize = 0; // 0 on 64-bit | 0 on 32-bit
|
||||||
|
pub const MEMORIES_OFFSET: usize = size_of::<TablesSlice>(); // 8 on 64-bit | 4 on 32-bit
|
||||||
|
pub const GLOBALS_OFFSET: usize = Instance::MEMORIES_OFFSET + size_of::<MemoriesSlice>(); // 16 on 64-bit | 8 on 32-bit
|
||||||
|
|
||||||
/// Create a new `Instance`.
|
/// Create a new `Instance`.
|
||||||
|
/// TODO: Raise an error when expected import is not part of imported object
|
||||||
|
/// Also make sure imports that are not declared do not get added to the instance
|
||||||
pub fn new(
|
pub fn new(
|
||||||
module: &Module,
|
module: &Module,
|
||||||
import_object: &ImportObject<&str, &str>,
|
import_object: ImportObject<&str, &str>,
|
||||||
options: InstanceOptions,
|
options: InstanceOptions,
|
||||||
) -> Result<Instance, ErrorKind> {
|
) -> Result<Instance, ErrorKind> {
|
||||||
let mut tables: Vec<Vec<usize>> = Vec::new();
|
let mut tables: Vec<Vec<usize>> = Vec::new();
|
||||||
let mut memories: Vec<LinearMemory> = Vec::new();
|
let mut memories: Vec<LinearMemory> = Vec::new();
|
||||||
let mut globals: Vec<u8> = Vec::new();
|
let mut globals: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
let mut functions: Vec<Vec<u8>> = Vec::new();
|
let mut functions: Vec<Vec<u8>> = Vec::new();
|
||||||
let mut import_functions: Vec<*const u8> = Vec::new();
|
let mut import_functions: Vec<*const u8> = Vec::new();
|
||||||
// let mut code_base: *const () = ptr::null();
|
|
||||||
|
let mut imported_memories: Vec<LinearMemory> = Vec::new();
|
||||||
|
let mut imported_tables: Vec<Vec<usize>> = Vec::new();
|
||||||
|
|
||||||
debug!("Instance - Instantiating functions");
|
debug!("Instance - Instantiating functions");
|
||||||
// Instantiate functions
|
// Instantiate functions
|
||||||
@ -166,25 +163,26 @@ impl Instance {
|
|||||||
// We walk through the imported functions and set the relocations
|
// We walk through the imported functions and set the relocations
|
||||||
// for each of this functions to be an empty vector (as is defined outside of wasm)
|
// for each of this functions to be an empty vector (as is defined outside of wasm)
|
||||||
for (module, field) in module.info.imported_funcs.iter() {
|
for (module, field) in module.info.imported_funcs.iter() {
|
||||||
let function = import_object.get(&module.as_str(), &field.as_str());
|
let imported = import_object
|
||||||
let function = if options.mock_missing_imports {
|
.get(&module.as_str(), &field.as_str());
|
||||||
function.unwrap_or_else(|| {
|
let function = match imported {
|
||||||
debug!(
|
Some(ImportValue::Func(f)) => f,
|
||||||
"The import {}.{} is not provided, therefore will be mocked.",
|
None => {
|
||||||
module, field
|
// if options.mock_missing_imports {
|
||||||
);
|
// debug!("The import {}.{} is not provided, therefore will be mocked.", module, field);
|
||||||
mock_fn as *const u8
|
// mock_fn as *const u8
|
||||||
})
|
// }
|
||||||
} else {
|
// else {
|
||||||
function.ok_or_else(|| {
|
return Err(ErrorKind::LinkError(format!(
|
||||||
ErrorKind::LinkError(format!(
|
|
||||||
"Imported function {}.{} was not provided in the import_functions",
|
"Imported function {}.{} was not provided in the import_functions",
|
||||||
module, field
|
module, field
|
||||||
))
|
)));
|
||||||
})?
|
// }
|
||||||
|
},
|
||||||
|
other => panic!("Expected function import, received {:?}", other)
|
||||||
};
|
};
|
||||||
// println!("GET FUNC {:?}", function);
|
// println!("GET FUNC {:?}", function);
|
||||||
import_functions.push(function);
|
import_functions.push(*function);
|
||||||
relocations.push(vec![]);
|
relocations.push(vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,61 +293,46 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We only want to allocate in memory if there is more than
|
// Looping through and getting the imported objects
|
||||||
// 0 functions. Otherwise reserving a 0-sized memory region
|
for (_key, value) in import_object.map {
|
||||||
// cause a panic error
|
match value {
|
||||||
// if total_size > 0 {
|
ImportValue::Memory(value) =>
|
||||||
// // Allocate the total memory for this functions
|
imported_memories.push(value),
|
||||||
// // let map = MmapMut::map_anon(total_size).unwrap();
|
ImportValue::Table(value) =>
|
||||||
// // let region_start = map.as_ptr() as usize;
|
imported_tables.push(value),
|
||||||
// // code_base = map.as_ptr() as *const ();
|
_ => (),
|
||||||
|
}
|
||||||
// // // Emit this functions to memory
|
|
||||||
// for (ref func_context, func_offset) in context_and_offsets.iter() {
|
|
||||||
// let mut trap_sink = TrapSink::new(*func_offset);
|
|
||||||
// let mut reloc_sink = RelocSink::new();
|
|
||||||
// let mut code_buf: Vec<u8> = Vec::new();
|
|
||||||
|
|
||||||
// // let mut func_pointer = as *mut u8;
|
|
||||||
// unsafe {
|
|
||||||
// func_context.emit_to_memory(
|
|
||||||
// &*isa,
|
|
||||||
// &mut code_buf,
|
|
||||||
// &mut reloc_sink,
|
|
||||||
// &mut trap_sink,
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
// let func_offset = code_buf.as_ptr() as usize;
|
|
||||||
// functions.push(*func_offset);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Set protection of this memory region to Read + Execute
|
|
||||||
// // so we are able to execute the functions emitted to memory
|
|
||||||
// // unsafe {
|
|
||||||
// // region::protect(region_start as *mut u8, total_size, region::Protection::ReadExecute)
|
|
||||||
// // .expect("unable to make memory readable+executable");
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Instance - Instantiating tables");
|
debug!("Instance - Instantiating tables");
|
||||||
// Instantiate tables
|
// Instantiate tables
|
||||||
{
|
{
|
||||||
// Reserve table space
|
// Reserve space for tables
|
||||||
tables.reserve_exact(module.info.tables.len());
|
tables.reserve_exact(imported_tables.len() + module.info.tables.len());
|
||||||
|
|
||||||
|
// Get imported tables
|
||||||
|
for table in imported_tables {
|
||||||
|
tables.push(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get tables in module
|
||||||
for table in &module.info.tables {
|
for table in &module.info.tables {
|
||||||
let len = table.entity.size;
|
let len = table.entity.size;
|
||||||
let mut v = Vec::with_capacity(len);
|
let mut v = Vec::with_capacity(len);
|
||||||
v.resize(len, 0);
|
v.resize(len, 0);
|
||||||
tables.push(v);
|
tables.push(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// instantiate tables
|
// instantiate tables
|
||||||
for table_element in &module.info.table_elements {
|
for table_element in &module.info.table_elements {
|
||||||
|
// TODO: We shouldn't assert here since we are returning a Result<Instance, ErrorKind>
|
||||||
assert!(
|
assert!(
|
||||||
table_element.base.is_none(),
|
table_element.base.is_none(),
|
||||||
"globalvalue base not supported yet."
|
"globalvalue base not supported yet."
|
||||||
);
|
);
|
||||||
|
|
||||||
let base = 0;
|
let base = 0;
|
||||||
|
|
||||||
let table = &mut tables[table_element.table_index.index()];
|
let table = &mut tables[table_element.table_index.index()];
|
||||||
@ -369,22 +352,24 @@ impl Instance {
|
|||||||
debug!("Instance - Instantiating memories");
|
debug!("Instance - Instantiating memories");
|
||||||
// Instantiate memories
|
// Instantiate memories
|
||||||
{
|
{
|
||||||
// Allocate the underlying memory and initialize it to all zeros.
|
// Reserve space for memories
|
||||||
let total_memories = module.info.memories.len();
|
memories.reserve_exact(imported_memories.len() + module.info.memories.len());
|
||||||
if total_memories > 0 {
|
|
||||||
memories.reserve_exact(total_memories);
|
// Get imported memories
|
||||||
for memory in &module.info.memories {
|
for memory in imported_memories {
|
||||||
let memory = memory.entity;
|
memories.push(memory);
|
||||||
let v = LinearMemory::new(
|
|
||||||
memory.pages_count as u32,
|
|
||||||
memory.maximum.map(|m| m as u32),
|
|
||||||
);
|
|
||||||
memories.push(v);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
memories.reserve_exact(1);
|
|
||||||
memories.push(LinearMemory::new(0, None));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get memories in module
|
||||||
|
for memory in &module.info.memories {
|
||||||
|
let memory = memory.entity;
|
||||||
|
let v = LinearMemory::new(
|
||||||
|
memory.pages_count as u32,
|
||||||
|
memory.maximum.map(|m| m as u32),
|
||||||
|
);
|
||||||
|
memories.push(v);
|
||||||
|
}
|
||||||
|
|
||||||
for init in &module.info.data_initializers {
|
for init in &module.info.data_initializers {
|
||||||
debug_assert!(init.base.is_none(), "globalvar base not supported yet");
|
debug_assert!(init.base.is_none(), "globalvar base not supported yet");
|
||||||
let offset = init.offset;
|
let offset = init.offset;
|
||||||
@ -395,6 +380,7 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug!("Instance - Instantiating globals");
|
debug!("Instance - Instantiating globals");
|
||||||
|
// TODO: Fix globals import
|
||||||
// Instantiate Globals
|
// Instantiate Globals
|
||||||
{
|
{
|
||||||
let globals_count = module.info.globals.len();
|
let globals_count = module.info.globals.len();
|
||||||
@ -406,6 +392,7 @@ impl Instance {
|
|||||||
let globals_data = unsafe {
|
let globals_data = unsafe {
|
||||||
slice::from_raw_parts_mut(globals.as_mut_ptr() as *mut i64, globals_count)
|
slice::from_raw_parts_mut(globals.as_mut_ptr() as *mut i64, globals_count)
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i, global) in module.info.globals.iter().enumerate() {
|
for (i, global) in module.info.globals.iter().enumerate() {
|
||||||
let value: i64 = match global.entity.initializer {
|
let value: i64 = match global.entity.initializer {
|
||||||
GlobalInit::I32Const(n) => n as _,
|
GlobalInit::I32Const(n) => n as _,
|
||||||
@ -439,9 +426,11 @@ impl Instance {
|
|||||||
// TODO: Refactor repetitive code
|
// TODO: Refactor repetitive code
|
||||||
let tables_pointer: Vec<BoundedSlice<usize>> =
|
let tables_pointer: Vec<BoundedSlice<usize>> =
|
||||||
tables.iter().map(|table| table[..].into()).collect();
|
tables.iter().map(|table| table[..].into()).collect();
|
||||||
let memories_pointer: Vec<UncheckedSlice<u8>> =
|
let memories_pointer: Vec<BoundedSlice<u8>> =
|
||||||
memories.iter().map(|mem| mem[..].into()).collect();
|
memories.iter().map(
|
||||||
let globals_pointer: UncheckedSlice<u8> = globals[..].into();
|
|mem| BoundedSlice::new(&mem[..], mem.current as usize * LinearMemory::WASM_PAGE_SIZE),
|
||||||
|
).collect();
|
||||||
|
let globals_pointer: GlobalsSlice = globals[..].into();
|
||||||
|
|
||||||
let data_pointers = DataPointers {
|
let data_pointers = DataPointers {
|
||||||
memories: memories_pointer[..].into(),
|
memories: memories_pointer[..].into(),
|
||||||
@ -449,18 +438,16 @@ impl Instance {
|
|||||||
tables: tables_pointer[..].into(),
|
tables: tables_pointer[..].into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let default_memory_bound = LinearMemory::WASM_PAGE_SIZE as i32;
|
// let mem = data_pointers.memories;
|
||||||
|
|
||||||
Ok(Instance {
|
Ok(Instance {
|
||||||
|
data_pointers,
|
||||||
tables: Arc::new(tables.into_iter().collect()), // tables.into_iter().map(|table| RwLock::new(table)).collect()),
|
tables: Arc::new(tables.into_iter().collect()), // tables.into_iter().map(|table| RwLock::new(table)).collect()),
|
||||||
memories: Arc::new(memories.into_iter().collect()),
|
memories: Arc::new(memories.into_iter().collect()),
|
||||||
globals,
|
globals,
|
||||||
functions,
|
functions,
|
||||||
import_functions,
|
import_functions,
|
||||||
start_func,
|
start_func,
|
||||||
data_pointers,
|
|
||||||
default_memory_bound,
|
|
||||||
// code_base: code_base,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,37 +475,6 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: To be removed.
|
|
||||||
// pub fn generate_context(&self) -> VmCtx {
|
|
||||||
// let memories: Vec<UncheckedSlice<u8>> =
|
|
||||||
// self.memories.iter().map(|mem| mem[..].into()).collect();
|
|
||||||
// let tables: Vec<BoundedSlice<usize>> =
|
|
||||||
// self.tables.iter().map(|table| table[..].into()).collect();
|
|
||||||
// let globals: UncheckedSlice<u8> = self.globals[..].into();
|
|
||||||
|
|
||||||
// // println!("GENERATING CONTEXT {:?}", self.globals);
|
|
||||||
|
|
||||||
// // assert!(memories.len() >= 1, "modules must have at least one memory");
|
|
||||||
// // the first memory has a space of `mem::size_of::<VmCtxData>()` rounded
|
|
||||||
// // up to the 4KiB before it. We write the VmCtxData into that.
|
|
||||||
// let instance = self.clone();
|
|
||||||
// VmCtx {
|
|
||||||
// globals: globals,
|
|
||||||
// memories: memories[..].into(),
|
|
||||||
// tables: tables[..].into(),
|
|
||||||
// user_data: UserData {
|
|
||||||
// // process,
|
|
||||||
// instance: instance,
|
|
||||||
// },
|
|
||||||
// phantom: PhantomData,
|
|
||||||
// }
|
|
||||||
// // let main_heap_ptr = memories[0].as_mut_ptr() as *mut VmCtxData;
|
|
||||||
// // unsafe {
|
|
||||||
// // main_heap_ptr.sub(1).write(data);
|
|
||||||
// // &*(main_heap_ptr as *const VmCtx)
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Returns a slice of the contents of allocated linear memory.
|
/// Returns a slice of the contents of allocated linear memory.
|
||||||
pub fn inspect_memory(&self, memory_index: usize, address: usize, len: usize) -> &[u8] {
|
pub fn inspect_memory(&self, memory_index: usize, address: usize, len: usize) -> &[u8] {
|
||||||
&self
|
&self
|
||||||
@ -540,46 +496,6 @@ impl Instance {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Instance {
|
|
||||||
fn clone(&self) -> Instance {
|
|
||||||
// TODO: Refactor repetitive code
|
|
||||||
let tables_pointer: Vec<BoundedSlice<usize>> =
|
|
||||||
self.tables.iter().map(|table| table[..].into()).collect();
|
|
||||||
let memories_pointer: Vec<UncheckedSlice<u8>> =
|
|
||||||
self.memories.iter().map(|mem| mem[..].into()).collect();
|
|
||||||
let globals_pointer: UncheckedSlice<u8> = self.globals[..].into();
|
|
||||||
|
|
||||||
let data_pointers = DataPointers {
|
|
||||||
memories: memories_pointer[..].into(),
|
|
||||||
globals: globals_pointer,
|
|
||||||
tables: tables_pointer[..].into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let default_memory_bound = self.memories.get(0).unwrap().current as i32;
|
|
||||||
|
|
||||||
Instance {
|
|
||||||
tables: Arc::clone(&self.tables),
|
|
||||||
memories: Arc::clone(&self.memories),
|
|
||||||
globals: self.globals.clone(),
|
|
||||||
functions: self.functions.clone(),
|
|
||||||
start_func: self.start_func.clone(),
|
|
||||||
import_functions: self.import_functions.clone(),
|
|
||||||
data_pointers,
|
|
||||||
default_memory_bound,
|
|
||||||
// code_base: self.code_base,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TODO:
|
|
||||||
/// Need to improve how memories are stored and grown.
|
|
||||||
/// Dynamic memory is inefficient both for growing and for access
|
|
||||||
/// Cranelift's dynamic heap assumes a _statically-known_ number of LinearMemories,
|
|
||||||
/// because it expects a corresponding global variable for each LinearMemory
|
|
||||||
///
|
|
||||||
/// Reference:
|
|
||||||
/// - https://cranelift.readthedocs.io/en/latest/ir.html?highlight=vmctx#heap-examples,
|
|
||||||
///
|
|
||||||
extern "C" fn grow_memory(size: u32, memory_index: u32, instance: &mut Instance) -> i32 {
|
extern "C" fn grow_memory(size: u32, memory_index: u32, instance: &mut Instance) -> i32 {
|
||||||
// TODO: Support for only one LinearMemory for now.
|
// TODO: Support for only one LinearMemory for now.
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
@ -590,33 +506,16 @@ extern "C" fn grow_memory(size: u32, memory_index: u32, instance: &mut Instance)
|
|||||||
let old_mem_size = instance
|
let old_mem_size = instance
|
||||||
.memory_mut(memory_index as usize)
|
.memory_mut(memory_index as usize)
|
||||||
.grow(size)
|
.grow(size)
|
||||||
.unwrap_or(i32::max_value()); // Should be -1 ?
|
.unwrap_or(-1);
|
||||||
|
|
||||||
// Update the default_memory_bound
|
if old_mem_size != -1 {
|
||||||
instance.default_memory_bound =
|
// Get new memory bytes
|
||||||
(instance.memories.get(0).unwrap().current as usize * LinearMemory::WASM_PAGE_SIZE) as i32;
|
let new_mem_bytes = (old_mem_size as usize + size as usize) * LinearMemory::WASM_PAGE_SIZE;
|
||||||
|
// Update data_pointer
|
||||||
|
instance.data_pointers.memories.get_unchecked_mut(memory_index as usize).len = new_mem_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
// The grown memory changed so data_pointers need to be updated as well.
|
old_mem_size
|
||||||
// TODO: Refactor repetitive code
|
|
||||||
let tables_pointer: Vec<BoundedSlice<usize>> = instance
|
|
||||||
.tables
|
|
||||||
.iter()
|
|
||||||
.map(|table| table[..].into())
|
|
||||||
.collect();
|
|
||||||
let memories_pointer: Vec<UncheckedSlice<u8>> =
|
|
||||||
instance.memories.iter().map(|mem| mem[..].into()).collect();
|
|
||||||
let globals_pointer: UncheckedSlice<u8> = instance.globals[..].into();
|
|
||||||
|
|
||||||
let data_pointers = DataPointers {
|
|
||||||
memories: memories_pointer[..].into(),
|
|
||||||
globals: globals_pointer,
|
|
||||||
tables: tables_pointer[..].into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update data_pointers
|
|
||||||
instance.data_pointers = data_pointers;
|
|
||||||
|
|
||||||
return old_mem_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn current_memory(memory_index: u32, instance: &mut Instance) -> u32 {
|
extern "C" fn current_memory(memory_index: u32, instance: &mut Instance) -> u32 {
|
||||||
|
@ -12,8 +12,6 @@ const MAX_PAGES: u32 = 65536;
|
|||||||
|
|
||||||
/// A linear memory instance.
|
/// A linear memory instance.
|
||||||
///
|
///
|
||||||
/// This linear memory has a stable base address and at the same time allows
|
|
||||||
/// for dynamical growing.
|
|
||||||
pub struct LinearMemory {
|
pub struct LinearMemory {
|
||||||
pub mmap: MmapMut,
|
pub mmap: MmapMut,
|
||||||
// The initial size of the WebAssembly Memory, in units of
|
// The initial size of the WebAssembly Memory, in units of
|
||||||
@ -25,7 +23,7 @@ pub struct LinearMemory {
|
|||||||
// front. However, the engine may ignore or clamp this reservation
|
// front. However, the engine may ignore or clamp this reservation
|
||||||
// request. In general, most WebAssembly modules shouldn't need
|
// request. In general, most WebAssembly modules shouldn't need
|
||||||
// to set a maximum.
|
// to set a maximum.
|
||||||
maximum: Option<u32>,
|
pub maximum: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// It holds the raw bytes of memory accessed by a WebAssembly Instance
|
/// It holds the raw bytes of memory accessed by a WebAssembly Instance
|
||||||
@ -33,7 +31,7 @@ impl LinearMemory {
|
|||||||
pub const WASM_PAGE_SIZE: usize = 1 << 16; // 64 KiB
|
pub const WASM_PAGE_SIZE: usize = 1 << 16; // 64 KiB
|
||||||
pub const DEFAULT_HEAP_SIZE: usize = 1 << 32; // 4 GiB
|
pub const DEFAULT_HEAP_SIZE: usize = 1 << 32; // 4 GiB
|
||||||
pub const DEFAULT_GUARD_SIZE: usize = 1 << 31; // 2 GiB
|
pub const DEFAULT_GUARD_SIZE: usize = 1 << 31; // 2 GiB
|
||||||
pub const DEFAULT_SIZE: usize = Self::DEFAULT_HEAP_SIZE + Self::DEFAULT_GUARD_SIZE; // 8GiB
|
pub const DEFAULT_SIZE: usize = Self::DEFAULT_HEAP_SIZE + Self::DEFAULT_GUARD_SIZE; // 6 GiB
|
||||||
|
|
||||||
/// Create a new linear memory instance with specified initial and maximum number of pages.
|
/// Create a new linear memory instance with specified initial and maximum number of pages.
|
||||||
///
|
///
|
||||||
@ -46,13 +44,9 @@ impl LinearMemory {
|
|||||||
initial, maximum
|
initial, maximum
|
||||||
);
|
);
|
||||||
|
|
||||||
let len: u64 = PAGE_SIZE as u64 * match maximum {
|
// TODO: Investigate if memory is zeroed out
|
||||||
Some(val) => val as u64,
|
let mmap = MmapMut::map_anon(LinearMemory::DEFAULT_HEAP_SIZE).unwrap();
|
||||||
None => initial as u64,
|
|
||||||
};
|
|
||||||
let len = if len == 0 { PAGE_SIZE as u64 } else { len };
|
|
||||||
|
|
||||||
let mmap = MmapMut::map_anon(len as usize).unwrap();
|
|
||||||
debug!("LinearMemory instantiated");
|
debug!("LinearMemory instantiated");
|
||||||
Self {
|
Self {
|
||||||
mmap,
|
mmap,
|
||||||
@ -96,37 +90,12 @@ impl LinearMemory {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let prev_bytes = self.mmap.len();
|
let prev_bytes = (prev_pages * PAGE_SIZE) as usize;
|
||||||
let new_bytes = (new_pages * PAGE_SIZE) as usize;
|
let new_bytes = (new_pages * PAGE_SIZE) as usize;
|
||||||
|
|
||||||
// Updating self.mmap if new_bytes > prev_bytes
|
// Updating self.current if new_bytes > prev_bytes
|
||||||
if new_bytes > prev_bytes {
|
if new_bytes > prev_bytes {
|
||||||
// If we have no maximum, this is a "dynamic" heap, and it's allowed
|
|
||||||
// to move.
|
|
||||||
let mut new_mmap = MmapMut::map_anon(new_bytes).unwrap();
|
|
||||||
|
|
||||||
// Copy old mem to new mem. Will a while loop be faster or is this going to be optimized?
|
|
||||||
// TODO: Consider static heap for efficiency.
|
|
||||||
for i in 0..prev_bytes {
|
|
||||||
unsafe {
|
|
||||||
let new_mmap_index = new_mmap.get_unchecked_mut(i);
|
|
||||||
let old_mmap_index = self.mmap.get_unchecked(i);
|
|
||||||
*new_mmap_index = *old_mmap_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zero out the remaining mem region
|
|
||||||
// TODO: Check if memmap zeroes out everything by default. This is very inefficient!
|
|
||||||
for i in prev_bytes..new_bytes {
|
|
||||||
unsafe {
|
|
||||||
let index = new_mmap.get_unchecked_mut(i);
|
|
||||||
*index = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Update relevant fields
|
|
||||||
self.mmap = new_mmap;
|
|
||||||
self.current = new_pages;
|
self.current = new_pages;
|
||||||
debug!("new memory = {:?}", self.mmap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(prev_pages as i32)
|
Some(prev_pages as i32)
|
||||||
@ -155,6 +124,14 @@ impl fmt::Debug for LinearMemory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not comparing based on memory content. That would be inefficient.
|
||||||
|
impl PartialEq for LinearMemory {
|
||||||
|
fn eq(&self, other: &LinearMemory) -> bool {
|
||||||
|
self.current == other.current &&
|
||||||
|
self.maximum == other.maximum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AsRef<[u8]> for LinearMemory {
|
impl AsRef<[u8]> for LinearMemory {
|
||||||
fn as_ref(&self) -> &[u8] {
|
fn as_ref(&self) -> &[u8] {
|
||||||
&self.mmap
|
&self.mmap
|
||||||
|
@ -16,7 +16,7 @@ use cranelift_codegen::{isa, settings};
|
|||||||
use cranelift_codegen::isa::TargetIsa;
|
use cranelift_codegen::isa::TargetIsa;
|
||||||
|
|
||||||
pub use self::errors::{Error, ErrorKind};
|
pub use self::errors::{Error, ErrorKind};
|
||||||
pub use self::import_object::ImportObject;
|
pub use self::import_object::{ImportObject, ImportValue};
|
||||||
pub use self::instance::{Instance, InstanceOptions};
|
pub use self::instance::{Instance, InstanceOptions};
|
||||||
pub use self::memory::LinearMemory;
|
pub use self::memory::LinearMemory;
|
||||||
pub use self::module::{Export, Module, ModuleInfo};
|
pub use self::module::{Export, Module, ModuleInfo};
|
||||||
@ -55,7 +55,7 @@ pub fn instantiate(
|
|||||||
debug!("webassembly - creating instance");
|
debug!("webassembly - creating instance");
|
||||||
let instance = Instance::new(
|
let instance = Instance::new(
|
||||||
&module,
|
&module,
|
||||||
&import_object,
|
import_object,
|
||||||
InstanceOptions {
|
InstanceOptions {
|
||||||
mock_missing_imports: true,
|
mock_missing_imports: true,
|
||||||
isa: isa
|
isa: isa
|
||||||
|
@ -35,6 +35,7 @@ use cranelift_wasm::{
|
|||||||
|
|
||||||
use super::errors::ErrorKind;
|
use super::errors::ErrorKind;
|
||||||
use super::memory::LinearMemory;
|
use super::memory::LinearMemory;
|
||||||
|
use super::instance::Instance;
|
||||||
|
|
||||||
/// Get the integer type used for representing pointers on this platform.
|
/// Get the integer type used for representing pointers on this platform.
|
||||||
fn native_pointer_type() -> ir::Type {
|
fn native_pointer_type() -> ir::Type {
|
||||||
@ -71,6 +72,7 @@ fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A collection of names under which a given entity is exported.
|
/// A collection of names under which a given entity is exported.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Exportable<T> {
|
pub struct Exportable<T> {
|
||||||
/// An entity.
|
/// An entity.
|
||||||
pub entity: T,
|
pub entity: T,
|
||||||
@ -101,6 +103,7 @@ pub enum Export {
|
|||||||
Global(GlobalIndex),
|
Global(GlobalIndex),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO: Need to get rid of unused field
|
||||||
/// The main state belonging to a `Module`. This is split out from
|
/// The main state belonging to a `Module`. This is split out from
|
||||||
/// `Module` to allow it to be borrowed separately from the
|
/// `Module` to allow it to be borrowed separately from the
|
||||||
/// `FuncTranslator` field.
|
/// `FuncTranslator` field.
|
||||||
@ -116,20 +119,21 @@ pub struct ModuleInfo {
|
|||||||
/// Signatures as provided by `declare_signature`.
|
/// Signatures as provided by `declare_signature`.
|
||||||
pub signatures: Vec<ir::Signature>,
|
pub signatures: Vec<ir::Signature>,
|
||||||
|
|
||||||
/// Module and field names of imported functions as provided by `declare_func_import`.
|
|
||||||
pub imported_funcs: Vec<(String, String)>,
|
|
||||||
|
|
||||||
/// Functions, imported and local.
|
/// Functions, imported and local.
|
||||||
pub functions: PrimaryMap<FuncIndex, Exportable<SignatureIndex>>,
|
pub functions: PrimaryMap<FuncIndex, Exportable<SignatureIndex>>,
|
||||||
|
|
||||||
/// Function bodies.
|
/// Function bodies.
|
||||||
pub function_bodies: PrimaryMap<DefinedFuncIndex, ir::Function>,
|
pub function_bodies: PrimaryMap<DefinedFuncIndex, ir::Function>,
|
||||||
|
|
||||||
|
/// Module and field names of imported functions as provided by `declare_func_import`.
|
||||||
|
pub imported_funcs: Vec<(String, String)>,
|
||||||
|
|
||||||
/// Tables as provided by `declare_table`.
|
/// Tables as provided by `declare_table`.
|
||||||
pub tables: Vec<Exportable<Table>>,
|
pub tables: Vec<Exportable<Table>>,
|
||||||
|
|
||||||
/// WebAssembly table initializers.
|
/// WebAssembly table initializers.
|
||||||
pub table_elements: Vec<TableElements>,
|
pub table_elements: Vec<TableElements>,
|
||||||
|
|
||||||
/// The base of tables.
|
/// The base of tables.
|
||||||
pub tables_base: Option<ir::GlobalValue>,
|
pub tables_base: Option<ir::GlobalValue>,
|
||||||
|
|
||||||
@ -166,9 +170,9 @@ impl ModuleInfo {
|
|||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
signatures: Vec::new(),
|
signatures: Vec::new(),
|
||||||
imported_funcs: Vec::new(),
|
|
||||||
functions: PrimaryMap::new(),
|
functions: PrimaryMap::new(),
|
||||||
function_bodies: PrimaryMap::new(),
|
function_bodies: PrimaryMap::new(),
|
||||||
|
imported_funcs: Vec::new(),
|
||||||
tables: Vec::new(),
|
tables: Vec::new(),
|
||||||
memories: Vec::new(),
|
memories: Vec::new(),
|
||||||
globals: Vec::new(),
|
globals: Vec::new(),
|
||||||
@ -352,149 +356,130 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
|
|||||||
self.target_config().pointer_bytes()
|
self.target_config().pointer_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: offsets should be based on the architecture the wasmer was compiled for.
|
||||||
|
// e.g., BoundedSlice.len will be 32-bit (4 bytes) when wasmer is compiled for a 32-bit arch,
|
||||||
|
// however the 32-bit wasmer may be running on 64-bit arch, which means ptr_size here will
|
||||||
|
// be 8 bytes. That will definitely gove the wrong offset values
|
||||||
fn make_table(&mut self, func: &mut ir::Function, table_index: TableIndex) -> ir::Table {
|
fn make_table(&mut self, func: &mut ir::Function, table_index: TableIndex) -> ir::Table {
|
||||||
assert_eq!(table_index.index(), 0, "Only one WebAssembly memory supported");
|
assert_eq!(table_index.index(), 0, "Only one WebAssembly memory supported");
|
||||||
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
|
let instance = func.create_global_value(ir::GlobalValueData::VMContext);
|
||||||
let ptr_size = native_pointer_size();
|
let ptr_size = native_pointer_size();
|
||||||
|
|
||||||
// Given a instance, we want to retrieve instance.tables
|
// Load value at (instance + TABLES_OFFSET)
|
||||||
// Create a table whose base address is stored at `instance+0`.
|
// which is the address of data_pointer.tables
|
||||||
// 0 is the offset of the vmctx.tables pointer respect to vmctx pointer
|
|
||||||
let base = func.create_global_value(ir::GlobalValueData::Load {
|
let base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: vmctx,
|
base: instance,
|
||||||
offset: offset32(native_pointer_size() as usize * 0),
|
offset: Offset32::new(Instance::TABLES_OFFSET as i32),
|
||||||
global_type: native_pointer_type(),
|
global_type: self.pointer_type(),
|
||||||
readonly: true,
|
readonly: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// This will be 0 when the index is 0, not sure if the offset will work regardless
|
// Offset based on table_index
|
||||||
// let table_data_offset = table_index.index() * (ptr_size as usize) * 2 + ptr_size;
|
let table_data_offset = table_index.index() as i32 * ptr_size * 2;
|
||||||
|
|
||||||
// We get the pointer for our table index
|
// Load value at the (base + table_data_offset)
|
||||||
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
|
// which is the address of data_pointer.tables[index].data
|
||||||
base: base,
|
let base_gv = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
offset: offset32(0),
|
base,
|
||||||
|
offset: Offset32::new(table_data_offset),
|
||||||
global_type: native_pointer_type(),
|
global_type: native_pointer_type(),
|
||||||
readonly: false,
|
readonly: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let base_gv = func.create_global_value(ir::GlobalValueData::Load {
|
// Load value at the (base + table_data_offset)
|
||||||
base: base,
|
// which is the value of data_pointer.tables[index].len
|
||||||
offset: offset32(ptr_size as usize),
|
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base,
|
||||||
|
offset: Offset32::new(table_data_offset + ptr_size as i32),
|
||||||
global_type: self.pointer_type(),
|
global_type: self.pointer_type(),
|
||||||
readonly: false
|
readonly: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Create table based on the data above
|
||||||
let table = func.create_table(ir::TableData {
|
let table = func.create_table(ir::TableData {
|
||||||
base_gv: base_gv,
|
base_gv,
|
||||||
min_size: Imm64::new(0),
|
min_size: Imm64::new(0),
|
||||||
bound_gv,
|
bound_gv,
|
||||||
element_size: Imm64::new(i64::from(self.pointer_bytes()) * 2),
|
element_size: Imm64::new(i64::from(self.pointer_bytes()) * 2),
|
||||||
index_type: I64,
|
index_type: I64,
|
||||||
});
|
});
|
||||||
// println!("FUNC {:?}", func);
|
|
||||||
table
|
table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: offsets should be based on the architecture the wasmer was compiled for.
|
||||||
|
// e.g., BoundedSlice.len will be 32-bit (4 bytes) when wasmer is compiled for a 32-bit arch,
|
||||||
|
// however the 32-bit wasmer may be running on 64-bit arch, which means ptr_size here will
|
||||||
|
// be 8 bytes. That will definitely gove the wrong offset values
|
||||||
fn make_heap(&mut self, func: &mut ir::Function, memory_index: MemoryIndex) -> ir::Heap {
|
fn make_heap(&mut self, func: &mut ir::Function, memory_index: MemoryIndex) -> ir::Heap {
|
||||||
assert_eq!(memory_index.index(), 0, "Only one WebAssembly memory supported");
|
debug_assert_eq!(memory_index.index(), 0, "Only one WebAssembly memory supported");
|
||||||
// Create a static heap whose base address is stored at `instance+8`.
|
let instance = func.create_global_value(ir::GlobalValueData::VMContext);
|
||||||
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
|
let ptr_size = native_pointer_size();
|
||||||
|
|
||||||
let heap_base_addr = func.create_global_value(ir::GlobalValueData::Load {
|
// Load value at (instance + MEMORIES_OFFSET)
|
||||||
base: vmctx,
|
// which is the address of data_pointer.memories
|
||||||
offset: offset32(native_pointer_size() as usize * 1),
|
let base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
global_type: native_pointer_type(),
|
base: instance,
|
||||||
|
offset: Offset32::new(Instance::MEMORIES_OFFSET as i32),
|
||||||
|
global_type: self.pointer_type(),
|
||||||
readonly: true,
|
readonly: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
let pointer_bytes = self.pointer_bytes() as usize;
|
// Based on the index provided, we need to know the offset into memories array
|
||||||
let memories_offset = memory_index.index() * pointer_bytes;
|
// Each element in the memories array has a size of (ptr_size * 2)
|
||||||
|
let memory_data_offset = 0; // (memory_index as usize * ptr_size * 2) as i32;
|
||||||
|
|
||||||
// We de-reference the vm_context.memories addr
|
// Load value at the (base + memory_data_offset)
|
||||||
|
// which is the address of data_pointer.memories[index].data
|
||||||
let heap_base = func.create_global_value(ir::GlobalValueData::Load {
|
let heap_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: heap_base_addr,
|
base,
|
||||||
offset: offset32(memories_offset),
|
offset: Offset32::new(memory_data_offset),
|
||||||
global_type: native_pointer_type(),
|
global_type: self.pointer_type(),
|
||||||
readonly: true,
|
readonly: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Load value at the (base + memory_data_offset)
|
||||||
|
// which is the value of data_pointer.memories[index].len
|
||||||
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
|
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: vmctx,
|
base,
|
||||||
offset: offset32(native_pointer_size() as usize * 3),
|
offset: Offset32::new(memory_data_offset + ptr_size as i32),
|
||||||
global_type: I32,
|
global_type: I32,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
func.create_heap(ir::HeapData {
|
// Create table based on the data above
|
||||||
|
let heap = func.create_heap(ir::HeapData {
|
||||||
base: heap_base,
|
base: heap_base,
|
||||||
min_size: Imm64::new(0),
|
min_size: 0.into(),
|
||||||
guard_size: Imm64::new(LinearMemory::DEFAULT_GUARD_SIZE as i64),
|
guard_size: (LinearMemory::DEFAULT_GUARD_SIZE as i64).into(),
|
||||||
style: ir::HeapStyle::Dynamic { bound_gv },
|
style: ir::HeapStyle::Dynamic {
|
||||||
index_type: I32,
|
bound_gv,
|
||||||
})
|
},
|
||||||
|
index_type: I32
|
||||||
|
});
|
||||||
|
|
||||||
// if index == 0 {
|
heap
|
||||||
// let heap_base = self.main_memory_base.unwrap_or_else(|| {
|
|
||||||
// let new_base = func.create_global_value(ir::GlobalValueData::VMContext {
|
|
||||||
// offset: 0.into(),
|
|
||||||
// });
|
|
||||||
// self.main_memory_base = Some(new_base);
|
|
||||||
// new_base
|
|
||||||
// });
|
|
||||||
|
|
||||||
// func.create_heap(ir::HeapData {
|
|
||||||
// base: heap_base,
|
|
||||||
// min_size: 0.into(),
|
|
||||||
// guard_size: (WasmMemory::DEFAULT_GUARD_SIZE as i64).into(),
|
|
||||||
// style: ir::HeapStyle::Static {
|
|
||||||
// bound: (WasmMemory::DEFAULT_HEAP_SIZE as i64).into(),
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
// let memory_base = self.memory_base.unwrap_or_else(|| {
|
|
||||||
// let memories_offset = self.ptr_size() as i32 * -2;
|
|
||||||
// let new_base = func.create_global_value(ir::GlobalValueData::VMContext {
|
|
||||||
// offset: memories_offset.into(),
|
|
||||||
// });
|
|
||||||
// self.memory_base = Some(new_base);
|
|
||||||
// new_base
|
|
||||||
// });
|
|
||||||
|
|
||||||
// let memory_offset = (index - 1) * self.ptr_size();
|
|
||||||
// let heap_base = func.create_global_value(ir::GlobalValueData::Deref {
|
|
||||||
// base: memory_base,
|
|
||||||
// offset: (memory_offset as i32).into(),
|
|
||||||
// });
|
|
||||||
|
|
||||||
// func.create_heap(ir::HeapData {
|
|
||||||
// base: heap_base,
|
|
||||||
// min_size: 0.into(),
|
|
||||||
// guard_size: (WasmMemory::DEFAULT_GUARD_SIZE as i64).into(),
|
|
||||||
// style: ir::HeapStyle::Static {
|
|
||||||
// bound: (WasmMemory::DEFAULT_HEAP_SIZE as i64).into(),
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_global(&mut self, func: &mut ir::Function, global_index: GlobalIndex) -> GlobalVariable {
|
fn make_global(&mut self, func: &mut ir::Function, global_index: GlobalIndex) -> GlobalVariable {
|
||||||
// Just create a dummy `vmctx` global.
|
let ptr_size = native_pointer_size();
|
||||||
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
|
|
||||||
|
let instance = func.create_global_value(ir::GlobalValueData::VMContext);
|
||||||
|
|
||||||
let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load {
|
let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: vmctx,
|
base: instance,
|
||||||
offset: offset32(native_pointer_size() as usize * 2),
|
offset: Offset32::new(Instance::GLOBALS_OFFSET as i32),
|
||||||
global_type: native_pointer_type(),
|
global_type: self.pointer_type(),
|
||||||
readonly: false,
|
readonly: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let offset = (global_index.index() * native_pointer_size() as usize) as i64;
|
let offset = global_index.index() as i64 * ptr_size as i64;
|
||||||
let iadd = func.create_global_value(ir::GlobalValueData::IAddImm {
|
let iadd = func.create_global_value(ir::GlobalValueData::IAddImm {
|
||||||
base: globals_base_addr,
|
base: globals_base_addr,
|
||||||
offset: Imm64::new(offset),
|
offset: Imm64::new(offset),
|
||||||
global_type: native_pointer_type(),
|
global_type: native_pointer_type(),
|
||||||
});
|
});
|
||||||
|
|
||||||
GlobalVariable::Memory {
|
GlobalVariable::Memory {
|
||||||
gv: iadd,
|
gv: iadd,
|
||||||
ty: self.mod_info.globals[global_index.index()].entity.ty,
|
ty: self.mod_info.globals[global_index.index()].entity.ty,
|
||||||
@ -537,6 +522,7 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
|
|||||||
.special_param(ir::ArgumentPurpose::VMContext)
|
.special_param(ir::ArgumentPurpose::VMContext)
|
||||||
.expect("Missing vmctx parameter");
|
.expect("Missing vmctx parameter");
|
||||||
|
|
||||||
|
|
||||||
// The `callee` value is an index into a table of function pointers.
|
// The `callee` value is an index into a table of function pointers.
|
||||||
// Apparently, that table is stored at absolute address 0 in this dummy environment.
|
// Apparently, that table is stored at absolute address 0 in this dummy environment.
|
||||||
// TODO: Generate bounds checking code.
|
// TODO: Generate bounds checking code.
|
||||||
@ -569,8 +555,6 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
|
|||||||
.CallIndirect(ir::Opcode::CallIndirect, INVALID, sig_ref, args)
|
.CallIndirect(ir::Opcode::CallIndirect, INVALID, sig_ref, args)
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
// println!("FUNC {:?}", pos.func);
|
|
||||||
|
|
||||||
Ok(inst)
|
Ok(inst)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,7 +572,7 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
|
|||||||
.expect("Missing vmctx parameter");
|
.expect("Missing vmctx parameter");
|
||||||
|
|
||||||
// println!("POINTER BYTES {}", self.pointer_bytes());
|
// println!("POINTER BYTES {}", self.pointer_bytes());
|
||||||
// println!("POINTER SIZE {}", self.ptr_size());
|
// println!("POINTER SIZE {}", native_pointer_size());
|
||||||
|
|
||||||
// Build a value list for the call instruction containing the call_args and the vmctx
|
// Build a value list for the call instruction containing the call_args and the vmctx
|
||||||
// parameter.
|
// parameter.
|
||||||
@ -757,8 +741,7 @@ impl<'data> ModuleEnvironment<'data> for Module {
|
|||||||
offset: usize,
|
offset: usize,
|
||||||
elements: Vec<FuncIndex>,
|
elements: Vec<FuncIndex>,
|
||||||
) {
|
) {
|
||||||
// NEW
|
debug_assert!(base.is_none(), "global-value offsets not supported yet");
|
||||||
// debug_assert!(base.is_none(), "global-value offsets not supported yet");
|
|
||||||
self.info.table_elements.push(TableElements {
|
self.info.table_elements.push(TableElements {
|
||||||
table_index,
|
table_index,
|
||||||
base,
|
base,
|
||||||
|
@ -9,31 +9,42 @@ pub fn is_wasm_binary(binary: &Vec<u8>) -> bool {
|
|||||||
|
|
||||||
pub fn print_instance_offsets(instance: &Instance) {
|
pub fn print_instance_offsets(instance: &Instance) {
|
||||||
let instance_address = instance as *const _ as usize;
|
let instance_address = instance as *const _ as usize;
|
||||||
|
let data_ptr = &instance.data_pointers;
|
||||||
|
|
||||||
let tables_pointer_address_ptr: *const usize =
|
let tables_pointer_address_ptr: *const usize =
|
||||||
unsafe { transmute(&instance.data_pointers.tables) };
|
unsafe { transmute(&data_ptr.tables) };
|
||||||
let tables_pointer_address = tables_pointer_address_ptr as usize;
|
let tables_pointer_address = tables_pointer_address_ptr as usize;
|
||||||
|
|
||||||
let memories_pointer_address_ptr: *const usize =
|
let memories_pointer_address_ptr: *const usize =
|
||||||
unsafe { transmute(&instance.data_pointers.memories) };
|
unsafe { transmute(&data_ptr.memories) };
|
||||||
let memories_pointer_address = memories_pointer_address_ptr as usize;
|
let memories_pointer_address = memories_pointer_address_ptr as usize;
|
||||||
|
|
||||||
let globals_pointer_address_ptr: *const usize =
|
let memories_pointer_address_ptr_0: *const usize =
|
||||||
unsafe { transmute(&instance.data_pointers.globals) };
|
unsafe { transmute(&data_ptr.memories.get_unchecked(0)) };
|
||||||
let globals_pointer_address = globals_pointer_address_ptr as usize;
|
let memories_pointer_address_0 = memories_pointer_address_ptr_0 as usize;
|
||||||
|
|
||||||
let default_memory_bound_address_ptr: *const usize =
|
let memories_pointer_address_ptr_0_data: *const usize =
|
||||||
unsafe { transmute(&instance.default_memory_bound) };
|
unsafe { transmute(&data_ptr.memories.get_unchecked(0).data) };
|
||||||
let default_memory_bound_address = default_memory_bound_address_ptr as usize;
|
let memories_pointer_address_0_data = memories_pointer_address_ptr_0_data as usize;
|
||||||
|
|
||||||
|
let memories_pointer_address_ptr_0_len: *const usize =
|
||||||
|
unsafe { transmute(&data_ptr.memories.get_unchecked(0).len) };
|
||||||
|
let memories_pointer_address_0_len = memories_pointer_address_ptr_0_len as usize;
|
||||||
|
|
||||||
|
let globals_pointer_address_ptr: *const usize =
|
||||||
|
unsafe { transmute(&data_ptr.globals) };
|
||||||
|
let globals_pointer_address = globals_pointer_address_ptr as usize;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"
|
"
|
||||||
====== INSTANCE OFFSET TABLE ======
|
====== INSTANCE OFFSET TABLE ======
|
||||||
instance \t\t\t- {:X} | offset - {:?}
|
instance \t\t\t- {:X} | offset - {:?}
|
||||||
instance.data_pointers.tables \t- {:X} | offset - {:?}
|
instance.data_pointers.tables \t- {:X} | offset - {:?}
|
||||||
instance.data_pointers.memories - {:X} | offset - {:?}
|
instance.data_pointers.memories\t- {:X} | offset - {:?}
|
||||||
|
.memories[0] \t\t- {:X} | offset - {:?}
|
||||||
|
.memories[0].data\t\t- {:X} | offset - {:?}
|
||||||
|
.memories[0].len({:?})\t- {:X} | offset - {:?}
|
||||||
instance.data_pointers.globals \t- {:X} | offset - {:?}
|
instance.data_pointers.globals \t- {:X} | offset - {:?}
|
||||||
instance.default_memory_bound \t- {:X} | offset - {:?}
|
|
||||||
====== INSTANCE OFFSET TABLE ======
|
====== INSTANCE OFFSET TABLE ======
|
||||||
",
|
",
|
||||||
instance_address,
|
instance_address,
|
||||||
@ -42,9 +53,16 @@ instance.default_memory_bound \t- {:X} | offset - {:?}
|
|||||||
tables_pointer_address - instance_address,
|
tables_pointer_address - instance_address,
|
||||||
memories_pointer_address,
|
memories_pointer_address,
|
||||||
memories_pointer_address - instance_address,
|
memories_pointer_address - instance_address,
|
||||||
|
|
||||||
|
memories_pointer_address_0,
|
||||||
|
0,
|
||||||
|
memories_pointer_address_0_data,
|
||||||
|
memories_pointer_address_0_data - memories_pointer_address_0_data,
|
||||||
|
data_ptr.memories.get_unchecked(0).len,
|
||||||
|
memories_pointer_address_0_len,
|
||||||
|
memories_pointer_address_0_len - memories_pointer_address_0_data,
|
||||||
|
|
||||||
globals_pointer_address,
|
globals_pointer_address,
|
||||||
globals_pointer_address - instance_address,
|
globals_pointer_address - instance_address,
|
||||||
default_memory_bound_address,
|
|
||||||
default_memory_bound_address - instance_address,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user