Repository: bytecodealliance/wasm-tools
Pull request #1728 (2024-08-20 18:40:07)

threads: add tests for `ref.i31_shared`
This change brings in Binaryen's existing [`shared-i31.wast`] test, minus the commented-out parts that Binaryen does not yet support. To get this to pass, we add the ability to parse `ref.i31_shared` as a constant expression in WAST.

[`shared-i31.wast`]: https://github.com/WebAssembly/binaryen/blob/main/test/spec/shared-i31.wast


Patch:
diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs
index e6615b16..fbcdbcbb 100644
--- a/crates/wasmparser/src/validator/core.rs
+++ b/crates/wasmparser/src/validator/core.rs
@@ -344,6 +344,20 @@ impl ModuleState {
                 }
             }
 
+            fn validate_shared_everything_threads(&mut self, op: &str) -> Result<()> {
+                if self.features.shared_everything_threads() {
+                    Ok(())
+                } else {
+                    Err(BinaryReaderError::new(
+                        format!(
+                            "constant expression required: non-constant operator: {}",
+                            op
+                        ),
+                        self.offset,
+                    ))
+                }
+            }
+
             fn validate_global(&mut self, index: u32) -> Result<()> {
                 let module = &self.resources.module;
                 let global = module.global_at(index, self.offset)?;
@@ -479,6 +493,10 @@ impl ModuleState {
                 $self.validate_gc("ref.i31")?;
                 $self.validator().visit_ref_i31()
             }};
+            (@visit $self:ident visit_ref_i31_shared) => {{
+                $self.validate_shared_everything_threads("ref.i31_shared")?;
+                $self.validator().visit_ref_i31_shared()
+            }};
 
             // `global.get` is a valid const expression for imported, immutable
             // globals.
diff --git a/crates/wast/src/core/wast.rs b/crates/wast/src/core/wast.rs
index 9dd9a923..d71350f7 100644
--- a/crates/wast/src/core/wast.rs
+++ b/crates/wast/src/core/wast.rs
@@ -90,6 +90,8 @@ pub enum WastRetCore<'a> {
     RefStruct,
     /// A non-null i31ref is expected.
     RefI31,
+    /// A non-null, shared i31ref is expected.
+    RefI31Shared,
 
     Either(Vec<WastRetCore<'a>>),
 }
@@ -111,6 +113,7 @@ static RETS: &[(&str, fn(Parser<'_>) -> Result<WastRetCore<'_>>)] = {
         ("ref.array", |_| Ok(RefArray)),
         ("ref.struct", |_| Ok(RefStruct)),
         ("ref.i31", |_| Ok(RefI31)),
+        ("ref.i31_shared", |_| Ok(RefI31Shared)),
         ("either", |p| {
             p.depth_check()?;
             let mut cases = Vec::new();
diff --git a/src/bin/wasm-tools/json_from_wast.rs b/src/bin/wasm-tools/json_from_wast.rs
index f37a459f..02ff170d 100644
--- a/src/bin/wasm-tools/json_from_wast.rs
+++ b/src/bin/wasm-tools/json_from_wast.rs
@@ -462,6 +462,7 @@ impl<'a> JsonBuilder<'a> {
             RefArray => json::Const::ArrayRef,
             RefStruct => json::Const::StructRef,
             RefI31 => json::Const::I31Ref,
+            RefI31Shared => json::Const::I31RefShared,
             Either(either) => json::Const::Either {
                 values: either
                     .into_iter()
@@ -676,6 +677,7 @@ mod json {
         ArrayRef,
         StructRef,
         I31Ref,
+        I31RefShared,
         // (ref.null none)
         NullRef,
         // (ref.null nofunc)


Test patch:
diff --git a/tests/local/shared-everything-threads/i31.wast b/tests/local/shared-everything-threads/i31.wast
index eec9a707..4d93ac73 100644
--- a/tests/local/shared-everything-threads/i31.wast
+++ b/tests/local/shared-everything-threads/i31.wast
@@ -1,5 +1,230 @@
 (module
-    (func (result (ref null (shared i31)))
-        (ref.i31_shared (i32.const 0))
-    )
+  (func (export "new") (param $i i32) (result (ref (shared i31)))
+    (ref.i31_shared (local.get $i))
+  )
+
+  (func (export "get_u") (param $i i32) (result i32)
+    (i31.get_u (ref.i31_shared (local.get $i)))
+  )
+  (func (export "get_s") (param $i i32) (result i32)
+    (i31.get_s (ref.i31_shared (local.get $i)))
+  )
+
+  (func (export "get_u-null") (result i32)
+    (i31.get_u (ref.null (shared i31)))
+  )
+  (func (export "get_s-null") (result i32)
+    (i31.get_u (ref.null (shared i31)))
+  )
+
+  (global $i (ref (shared i31)) (ref.i31_shared (i32.const 2)))
+  (global $m (mut (ref (shared i31))) (ref.i31_shared (i32.const 3)))
+
+  (func (export "get_globals") (result i32 i32)
+    (i31.get_u (global.get $i))
+    (i31.get_u (global.get $m))
+  )
+
+  (func (export "set_global") (param i32)
+    (global.set $m (ref.i31_shared (local.get 0)))
+  )
 )
+
+(assert_return (invoke "new" (i32.const 1)) (ref.i31_shared))
+
+(assert_return (invoke "get_u" (i32.const 0)) (i32.const 0))
+(assert_return (invoke "get_u" (i32.const 100)) (i32.const 100))
+(assert_return (invoke "get_u" (i32.const -1)) (i32.const 0x7fff_ffff))
+(assert_return (invoke "get_u" (i32.const 0x3fff_ffff)) (i32.const 0x3fff_ffff))
+(assert_return (invoke "get_u" (i32.const 0x4000_0000)) (i32.const 0x4000_0000))
+(assert_return (invoke "get_u" (i32.const 0x7fff_ffff)) (i32.const 0x7fff_ffff))
+(assert_return (invoke "get_u" (i32.const 0xaaaa_aaaa)) (i32.const 0x2aaa_aaaa))
+(assert_return (invoke "get_u" (i32.const 0xcaaa_aaaa)) (i32.const 0x4aaa_aaaa))
+
+(assert_return (invoke "get_s" (i32.const 0)) (i32.const 0))
+(assert_return (invoke "get_s" (i32.const 100)) (i32.const 100))
+(assert_return (invoke "get_s" (i32.const -1)) (i32.const -1))
+(assert_return (invoke "get_s" (i32.const 0x3fff_ffff)) (i32.const 0x3fff_ffff))
+(assert_return (invoke "get_s" (i32.const 0x4000_0000)) (i32.const -0x4000_0000))
+(assert_return (invoke "get_s" (i32.const 0x7fff_ffff)) (i32.const -1))
+(assert_return (invoke "get_s" (i32.const 0xaaaa_aaaa)) (i32.const 0x2aaa_aaaa))
+(assert_return (invoke "get_s" (i32.const 0xcaaa_aaaa)) (i32.const 0xcaaa_aaaa))
+
+(assert_trap (invoke "get_u-null") "null i31 reference")
+(assert_trap (invoke "get_s-null") "null i31 reference")
+
+(assert_return (invoke "get_globals") (i32.const 2) (i32.const 3))
+
+(invoke "set_global" (i32.const 1234))
+(assert_return (invoke "get_globals") (i32.const 2) (i32.const 1234))
+
+(module $tables_of_i31ref
+  (table $table 3 10 (ref null (shared i31)))
+  (elem (table $table) (i32.const 0) (ref null (shared i31))
+      (item (ref.i31_shared (i32.const 999)))
+      (item (ref.i31_shared (i32.const 888)))
+      (item (ref.i31_shared (i32.const 777))))
+
+  (func (export "size") (result i32)
+    table.size $table
+  )
+
+  (func (export "get") (param i32) (result i32)
+    (i31.get_u (table.get $table (local.get 0)))
+  )
+
+  (func (export "grow") (param i32 i32) (result i32)
+    (table.grow $table (ref.i31_shared (local.get 1)) (local.get 0))
+  )
+
+  (func (export "fill") (param i32 i32 i32)
+    (table.fill $table (local.get 0) (ref.i31_shared (local.get 1)) (local.get 2))
+  )
+
+  (func (export "copy") (param i32 i32 i32)
+    (table.copy $table $table (local.get 0) (local.get 1) (local.get 2))
+  )
+
+  (elem $elem (ref null (shared i31)) (item (ref.i31_shared (i32.const 123)))
+                                      (item (ref.i31_shared (i32.const 456)))
+                                      (item (ref.i31_shared (i32.const 789))))
+  (func (export "init") (param i32 i32 i32)
+    (table.init $table $elem (local.get 0) (local.get 1) (local.get 2))
+  )
+)
+
+;; Initial state.
+(assert_return (invoke "size") (i32.const 3))
+(assert_return (invoke "get" (i32.const 0)) (i32.const 999))
+(assert_return (invoke "get" (i32.const 1)) (i32.const 888))
+(assert_return (invoke "get" (i32.const 2)) (i32.const 777))
+
+;; Grow from size 3 to size 5.
+(assert_return (invoke "grow" (i32.const 2) (i32.const 333)) (i32.const 3))
+(assert_return (invoke "size") (i32.const 5))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 333))
+(assert_return (invoke "get" (i32.const 4)) (i32.const 333))
+
+;; Fill table[2..4] = 111.
+(invoke "fill" (i32.const 2) (i32.const 111) (i32.const 2))
+(assert_return (invoke "get" (i32.const 2)) (i32.const 111))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 111))
+
+;; Copy from table[0..2] to table[3..5].
+(invoke "copy" (i32.const 3) (i32.const 0) (i32.const 2))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 999))
+(assert_return (invoke "get" (i32.const 4)) (i32.const 888))
+
+;; Initialize the passive element at table[1..4].
+(invoke "init" (i32.const 1) (i32.const 0) (i32.const 3))
+(assert_return (invoke "get" (i32.const 1)) (i32.const 123))
+(assert_return (invoke "get" (i32.const 2)) (i32.const 456))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 789))
+
+(module $env
+  (global (export "g") i32 (i32.const 42))
+)
+(register "env")
+
+(module $i31ref_of_global_table_initializer
+  (global $g (import "env" "g") i32)
+  (table $t 3 3 (ref (shared i31)) (ref.i31_shared (global.get $g)))
+  (func (export "get") (param i32) (result i32)
+    (i31.get_u (local.get 0) (table.get $t))
+  )
+)
+
+(assert_return (invoke "get" (i32.const 0)) (i32.const 42))
+(assert_return (invoke "get" (i32.const 1)) (i32.const 42))
+(assert_return (invoke "get" (i32.const 2)) (i32.const 42))
+
+(module $i31ref_of_global_global_initializer
+  (global $g0 (import "env" "g") i32)
+  (global $g1 (ref null (shared i31)) (ref.i31_shared (global.get $g0)))
+  (func (export "get") (result i32)
+    (i31.get_u (global.get $g1))
+  )
+)
+
+(assert_return (invoke "get") (i32.const 42))
+
+(module $anyref_global_of_i31ref
+  (global $c (ref null (shared any)) (ref.i31_shared (i32.const 1234)))
+  (global $m (mut (ref null (shared any))) (ref.i31_shared (i32.const 5678)))
+
+  (func (export "get_globals") (result i32 i32)
+    (i31.get_u (ref.cast (ref null (shared i31)) (global.get $c)))
+    (i31.get_u (ref.cast (ref null (shared i31)) (global.get $m)))
+  )
+
+  (func (export "set_global") (param i32)
+    (global.set $m (ref.i31_shared (local.get 0)))
+  )
+)
+
+(assert_return (invoke "get_globals") (i32.const 1234) (i32.const 5678))
+(invoke "set_global" (i32.const 0))
+(assert_return (invoke "get_globals") (i32.const 1234) (i32.const 0))
+
+(module $anyref_table_of_i31ref
+  (table $table 3 10 (ref null (shared any)))
+  (elem (table $table) (i32.const 0) (ref null (shared i31))
+      (item (ref.i31_shared (i32.const 999)))
+      (item (ref.i31_shared (i32.const 888)))
+      (item (ref.i31_shared (i32.const 777))))
+
+  (func (export "size") (result i32)
+    table.size $table
+  )
+
+  (func (export "get") (param i32) (result i32)
+    (i31.get_u (ref.cast (ref null (shared i31)) (table.get $table (local.get 0))))
+  )
+
+  (func (export "grow") (param i32 i32) (result i32)
+    (table.grow $table (ref.i31_shared (local.get 1)) (local.get 0))
+  )
+
+  (func (export "fill") (param i32 i32 i32)
+    (table.fill $table (local.get 0) (ref.i31_shared (local.get 1)) (local.get 2))
+  )
+
+  (func (export "copy") (param i32 i32 i32)
+    (table.copy $table $table (local.get 0) (local.get 1) (local.get 2))
+  )
+
+  (elem $elem (ref null (shared i31)) (item (ref.i31_shared (i32.const 123)))
+                     (item (ref.i31_shared (i32.const 456)))
+                     (item (ref.i31_shared (i32.const 789))))
+  (func (export "init") (param i32 i32 i32)
+    (table.init $table $elem (local.get 0) (local.get 1) (local.get 2))
+  )
+)
+
+;; Initial state.
+(assert_return (invoke "size") (i32.const 3))
+(assert_return (invoke "get" (i32.const 0)) (i32.const 999))
+(assert_return (invoke "get" (i32.const 1)) (i32.const 888))
+(assert_return (invoke "get" (i32.const 2)) (i32.const 777))
+
+;; Grow from size 3 to size 5.
+(assert_return (invoke "grow" (i32.const 2) (i32.const 333)) (i32.const 3))
+(assert_return (invoke "size") (i32.const 5))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 333))
+(assert_return (invoke "get" (i32.const 4)) (i32.const 333))
+
+;; Fill table[2..4] = 111.
+(invoke "fill" (i32.const 2) (i32.const 111) (i32.const 2))
+(assert_return (invoke "get" (i32.const 2)) (i32.const 111))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 111))
+
+;; Copy from table[0..2] to table[3..5].
+(invoke "copy" (i32.const 3) (i32.const 0) (i32.const 2))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 999))
+(assert_return (invoke "get" (i32.const 4)) (i32.const 888))
+
+;; Initialize the passive element at table[1..4].
+(invoke "init" (i32.const 1) (i32.const 0) (i32.const 3))
+(assert_return (invoke "get" (i32.const 1)) (i32.const 123))
+(assert_return (invoke "get" (i32.const 2)) (i32.const 456))
+(assert_return (invoke "get" (i32.const 3)) (i32.const 789))
diff --git a/tests/snapshots/local/shared-everything-threads/i31.wast.json b/tests/snapshots/local/shared-everything-threads/i31.wast.json
index 7c5e72a7..3e0878b1 100644
--- a/tests/snapshots/local/shared-everything-threads/i31.wast.json
+++ b/tests/snapshots/local/shared-everything-threads/i31.wast.json
@@ -5,6 +5,1305 @@
       "type": "module",
       "line": 1,
       "filename": "i31.0.wasm"
+    },
+    {
+      "type": "assert_return",
+      "line": 33,
+      "action": {
+        "type": "invoke",
+        "field": "new",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i31refshared"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 35,
+      "action": {
+        "type": "invoke",
+        "field": "get_u",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 36,
+      "action": {
+        "type": "invoke",
+        "field": "get_u",
+        "args": [
+          {
+            "type": "i32",
+            "value": "100"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "100"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 37,
+      "action": {
+        "type": "invoke",
+        "field": "get_u",
+        "args": [
+          {
+            "type": "i32",
+            "value": "-1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "2147483647"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 38,
+      "action": {
+        "type": "invoke",
+        "field": "get_u",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1073741823"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1073741823"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 39,
+      "action": {
+        "type": "invoke",
+        "field": "get_u",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1073741824"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1073741824"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 40,
+      "action": {
+        "type": "invoke",
+        "field": "get_u",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2147483647"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "2147483647"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 41,
+      "action": {
+        "type": "invoke",
+        "field": "get_u",
+        "args": [
+          {
+            "type": "i32",
+            "value": "-1431655766"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "715827882"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 42,
+      "action": {
+        "type": "invoke",
+        "field": "get_u",
+        "args": [
+          {
+            "type": "i32",
+            "value": "-894784854"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1252698794"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 44,
+      "action": {
+        "type": "invoke",
+        "field": "get_s",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 45,
+      "action": {
+        "type": "invoke",
+        "field": "get_s",
+        "args": [
+          {
+            "type": "i32",
+            "value": "100"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "100"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 46,
+      "action": {
+        "type": "invoke",
+        "field": "get_s",
+        "args": [
+          {
+            "type": "i32",
+            "value": "-1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "-1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 47,
+      "action": {
+        "type": "invoke",
+        "field": "get_s",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1073741823"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1073741823"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 48,
+      "action": {
+        "type": "invoke",
+        "field": "get_s",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1073741824"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "-1073741824"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 49,
+      "action": {
+        "type": "invoke",
+        "field": "get_s",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2147483647"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "-1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 50,
+      "action": {
+        "type": "invoke",
+        "field": "get_s",
+        "args": [
+          {
+            "type": "i32",
+            "value": "-1431655766"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "715827882"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 51,
+      "action": {
+        "type": "invoke",
+        "field": "get_s",
+        "args": [
+          {
+            "type": "i32",
+            "value": "-894784854"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "-894784854"
+        }
+      ]
+    },
+    {
+      "type": "assert_trap",
+      "line": 53,
+      "action": {
+        "type": "invoke",
+        "field": "get_u-null",
+        "args": []
+      },
+      "text": "null i31 reference"
+    },
+    {
+      "type": "assert_trap",
+      "line": 54,
+      "action": {
+        "type": "invoke",
+        "field": "get_s-null",
+        "args": []
+      },
+      "text": "null i31 reference"
+    },
+    {
+      "type": "assert_return",
+      "line": 56,
+      "action": {
+        "type": "invoke",
+        "field": "get_globals",
+        "args": []
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "2"
+        },
+        {
+          "type": "i32",
+          "value": "3"
+        }
+      ]
+    },
+    {
+      "type": "action",
+      "line": 58,
+      "action": {
+        "type": "invoke",
+        "field": "set_global",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1234"
+          }
+        ]
+      }
+    },
+    {
+      "type": "assert_return",
+      "line": 59,
+      "action": {
+        "type": "invoke",
+        "field": "get_globals",
+        "args": []
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "2"
+        },
+        {
+          "type": "i32",
+          "value": "1234"
+        }
+      ]
+    },
+    {
+      "type": "module",
+      "line": 61,
+      "name": "tables_of_i31ref",
+      "filename": "i31.1.wasm"
+    },
+    {
+      "type": "assert_return",
+      "line": 97,
+      "action": {
+        "type": "invoke",
+        "field": "size",
+        "args": []
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "3"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 98,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "999"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 99,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "888"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 100,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "777"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 103,
+      "action": {
+        "type": "invoke",
+        "field": "grow",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "333"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "3"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 104,
+      "action": {
+        "type": "invoke",
+        "field": "size",
+        "args": []
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "5"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 105,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "333"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 106,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "333"
+        }
+      ]
+    },
+    {
+      "type": "action",
+      "line": 109,
+      "action": {
+        "type": "invoke",
+        "field": "fill",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "111"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      }
+    },
+    {
+      "type": "assert_return",
+      "line": 110,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "111"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 111,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "111"
+        }
+      ]
+    },
+    {
+      "type": "action",
+      "line": 114,
+      "action": {
+        "type": "invoke",
+        "field": "copy",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      }
+    },
+    {
+      "type": "assert_return",
+      "line": 115,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "999"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 116,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "888"
+        }
+      ]
+    },
+    {
+      "type": "action",
+      "line": 119,
+      "action": {
+        "type": "invoke",
+        "field": "init",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      }
+    },
+    {
+      "type": "assert_return",
+      "line": 120,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "123"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 121,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "456"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 122,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "789"
+        }
+      ]
+    },
+    {
+      "type": "module",
+      "line": 124,
+      "name": "env",
+      "filename": "i31.2.wasm"
+    },
+    {
+      "type": "register",
+      "line": 127,
+      "as": "env"
+    },
+    {
+      "type": "module",
+      "line": 129,
+      "name": "i31ref_of_global_table_initializer",
+      "filename": "i31.3.wasm"
+    },
+    {
+      "type": "assert_return",
+      "line": 137,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "42"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 138,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "42"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 139,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "42"
+        }
+      ]
+    },
+    {
+      "type": "module",
+      "line": 141,
+      "name": "i31ref_of_global_global_initializer",
+      "filename": "i31.4.wasm"
+    },
+    {
+      "type": "assert_return",
+      "line": 149,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": []
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "42"
+        }
+      ]
+    },
+    {
+      "type": "module",
+      "line": 151,
+      "name": "anyref_global_of_i31ref",
+      "filename": "i31.5.wasm"
+    },
+    {
+      "type": "assert_return",
+      "line": 165,
+      "action": {
+        "type": "invoke",
+        "field": "get_globals",
+        "args": []
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1234"
+        },
+        {
+          "type": "i32",
+          "value": "5678"
+        }
+      ]
+    },
+    {
+      "type": "action",
+      "line": 166,
+      "action": {
+        "type": "invoke",
+        "field": "set_global",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      }
+    },
+    {
+      "type": "assert_return",
+      "line": 167,
+      "action": {
+        "type": "invoke",
+        "field": "get_globals",
+        "args": []
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1234"
+        },
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "module",
+      "line": 169,
+      "name": "anyref_table_of_i31ref",
+      "filename": "i31.6.wasm"
+    },
+    {
+      "type": "assert_return",
+      "line": 205,
+      "action": {
+        "type": "invoke",
+        "field": "size",
+        "args": []
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "3"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 206,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "999"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 207,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "888"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 208,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "777"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 211,
+      "action": {
+        "type": "invoke",
+        "field": "grow",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "333"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "3"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 212,
+      "action": {
+        "type": "invoke",
+        "field": "size",
+        "args": []
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "5"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 213,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "333"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 214,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "333"
+        }
+      ]
+    },
+    {
+      "type": "action",
+      "line": 217,
+      "action": {
+        "type": "invoke",
+        "field": "fill",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "111"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      }
+    },
+    {
+      "type": "assert_return",
+      "line": 218,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "111"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 219,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "111"
+        }
+      ]
+    },
+    {
+      "type": "action",
+      "line": 222,
+      "action": {
+        "type": "invoke",
+        "field": "copy",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      }
+    },
+    {
+      "type": "assert_return",
+      "line": 223,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "999"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 224,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "888"
+        }
+      ]
+    },
+    {
+      "type": "action",
+      "line": 227,
+      "action": {
+        "type": "invoke",
+        "field": "init",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      }
+    },
+    {
+      "type": "assert_return",
+      "line": 228,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "123"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 229,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "456"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 230,
+      "action": {
+        "type": "invoke",
+        "field": "get",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "789"
+        }
+      ]
     }
   ]
 }
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/i31.wast/0.print b/tests/snapshots/local/shared-everything-threads/i31.wast/0.print
index d9d704e3..24e77e86 100644
--- a/tests/snapshots/local/shared-everything-threads/i31.wast/0.print
+++ b/tests/snapshots/local/shared-everything-threads/i31.wast/0.print
@@ -1,7 +1,49 @@
 (module
-  (type (;0;) (func (result (ref null (shared i31)))))
-  (func (;0;) (type 0) (result (ref null (shared i31)))
-    i32.const 0
+  (type (;0;) (func (param i32) (result (ref (shared i31)))))
+  (type (;1;) (func (param i32) (result i32)))
+  (type (;2;) (func (result i32)))
+  (type (;3;) (func (result i32 i32)))
+  (type (;4;) (func (param i32)))
+  (func (;0;) (type 0) (param $i i32) (result (ref (shared i31)))
+    local.get $i
     ref.i31_shared
   )
+  (func (;1;) (type 1) (param $i i32) (result i32)
+    local.get $i
+    ref.i31_shared
+    i31.get_u
+  )
+  (func (;2;) (type 1) (param $i i32) (result i32)
+    local.get $i
+    ref.i31_shared
+    i31.get_s
+  )
+  (func (;3;) (type 2) (result i32)
+    ref.null (shared i31)
+    i31.get_u
+  )
+  (func (;4;) (type 2) (result i32)
+    ref.null (shared i31)
+    i31.get_u
+  )
+  (func (;5;) (type 3) (result i32 i32)
+    global.get $i
+    i31.get_u
+    global.get $m
+    i31.get_u
+  )
+  (func (;6;) (type 4) (param i32)
+    local.get 0
+    ref.i31_shared
+    global.set $m
+  )
+  (global $i (;0;) (ref (shared i31)) i32.const 2 ref.i31_shared)
+  (global $m (;1;) (mut (ref (shared i31))) i32.const 3 ref.i31_shared)
+  (export "new" (func 0))
+  (export "get_u" (func 1))
+  (export "get_s" (func 2))
+  (export "get_u-null" (func 3))
+  (export "get_s-null" (func 4))
+  (export "get_globals" (func 5))
+  (export "set_global" (func 6))
 )
diff --git a/tests/snapshots/local/shared-everything-threads/i31.wast/23.print b/tests/snapshots/local/shared-everything-threads/i31.wast/23.print
new file mode 100644
index 00000000..8b37d4bd
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/i31.wast/23.print
@@ -0,0 +1,48 @@
+(module $tables_of_i31ref
+  (type (;0;) (func (result i32)))
+  (type (;1;) (func (param i32) (result i32)))
+  (type (;2;) (func (param i32 i32) (result i32)))
+  (type (;3;) (func (param i32 i32 i32)))
+  (func (;0;) (type 0) (result i32)
+    table.size $table
+  )
+  (func (;1;) (type 1) (param i32) (result i32)
+    local.get 0
+    table.get $table
+    i31.get_u
+  )
+  (func (;2;) (type 2) (param i32 i32) (result i32)
+    local.get 1
+    ref.i31_shared
+    local.get 0
+    table.grow $table
+  )
+  (func (;3;) (type 3) (param i32 i32 i32)
+    local.get 0
+    local.get 1
+    ref.i31_shared
+    local.get 2
+    table.fill $table
+  )
+  (func (;4;) (type 3) (param i32 i32 i32)
+    local.get 0
+    local.get 1
+    local.get 2
+    table.copy
+  )
+  (func (;5;) (type 3) (param i32 i32 i32)
+    local.get 0
+    local.get 1
+    local.get 2
+    table.init $elem
+  )
+  (table $table (;0;) 3 10 (ref null (shared i31)))
+  (export "size" (func 0))
+  (export "get" (func 1))
+  (export "grow" (func 2))
+  (export "fill" (func 3))
+  (export "copy" (func 4))
+  (export "init" (func 5))
+  (elem (;0;) (i32.const 0) (ref null (shared i31)) (item i32.const 999 ref.i31_shared) (item i32.const 888 ref.i31_shared) (item i32.const 777 ref.i31_shared))
+  (elem $elem (;1;) (ref null (shared i31)) (item i32.const 123 ref.i31_shared) (item i32.const 456 ref.i31_shared) (item i32.const 789 ref.i31_shared))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/i31.wast/42.print b/tests/snapshots/local/shared-everything-threads/i31.wast/42.print
new file mode 100644
index 00000000..7e5820fe
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/i31.wast/42.print
@@ -0,0 +1,4 @@
+(module $env
+  (global (;0;) i32 i32.const 42)
+  (export "g" (global 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/i31.wast/44.print b/tests/snapshots/local/shared-everything-threads/i31.wast/44.print
new file mode 100644
index 00000000..687223b6
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/i31.wast/44.print
@@ -0,0 +1,11 @@
+(module $i31ref_of_global_table_initializer
+  (type (;0;) (func (param i32) (result i32)))
+  (import "env" "g" (global $g (;0;) i32))
+  (func (;0;) (type 0) (param i32) (result i32)
+    local.get 0
+    table.get $t
+    i31.get_u
+  )
+  (table $t (;0;) 3 3 (ref (shared i31)) global.get $g ref.i31_shared)
+  (export "get" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/i31.wast/48.print b/tests/snapshots/local/shared-everything-threads/i31.wast/48.print
new file mode 100644
index 00000000..639fcee4
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/i31.wast/48.print
@@ -0,0 +1,10 @@
+(module $i31ref_of_global_global_initializer
+  (type (;0;) (func (result i32)))
+  (import "env" "g" (global $g0 (;0;) i32))
+  (func (;0;) (type 0) (result i32)
+    global.get $g1
+    i31.get_u
+  )
+  (global $g1 (;1;) (ref null (shared i31)) global.get $g0 ref.i31_shared)
+  (export "get" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/i31.wast/50.print b/tests/snapshots/local/shared-everything-threads/i31.wast/50.print
new file mode 100644
index 00000000..f810f1ad
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/i31.wast/50.print
@@ -0,0 +1,21 @@
+(module $anyref_global_of_i31ref
+  (type (;0;) (func (result i32 i32)))
+  (type (;1;) (func (param i32)))
+  (func (;0;) (type 0) (result i32 i32)
+    global.get $c
+    ref.cast (ref null (shared i31))
+    i31.get_u
+    global.get $m
+    ref.cast (ref null (shared i31))
+    i31.get_u
+  )
+  (func (;1;) (type 1) (param i32)
+    local.get 0
+    ref.i31_shared
+    global.set $m
+  )
+  (global $c (;0;) (ref null (shared any)) i32.const 1234 ref.i31_shared)
+  (global $m (;1;) (mut (ref null (shared any))) i32.const 5678 ref.i31_shared)
+  (export "get_globals" (func 0))
+  (export "set_global" (func 1))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/i31.wast/54.print b/tests/snapshots/local/shared-everything-threads/i31.wast/54.print
new file mode 100644
index 00000000..c7b8a662
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/i31.wast/54.print
@@ -0,0 +1,49 @@
+(module $anyref_table_of_i31ref
+  (type (;0;) (func (result i32)))
+  (type (;1;) (func (param i32) (result i32)))
+  (type (;2;) (func (param i32 i32) (result i32)))
+  (type (;3;) (func (param i32 i32 i32)))
+  (func (;0;) (type 0) (result i32)
+    table.size $table
+  )
+  (func (;1;) (type 1) (param i32) (result i32)
+    local.get 0
+    table.get $table
+    ref.cast (ref null (shared i31))
+    i31.get_u
+  )
+  (func (;2;) (type 2) (param i32 i32) (result i32)
+    local.get 1
+    ref.i31_shared
+    local.get 0
+    table.grow $table
+  )
+  (func (;3;) (type 3) (param i32 i32 i32)
+    local.get 0
+    local.get 1
+    ref.i31_shared
+    local.get 2
+    table.fill $table
+  )
+  (func (;4;) (type 3) (param i32 i32 i32)
+    local.get 0
+    local.get 1
+    local.get 2
+    table.copy
+  )
+  (func (;5;) (type 3) (param i32 i32 i32)
+    local.get 0
+    local.get 1
+    local.get 2
+    table.init $elem
+  )
+  (table $table (;0;) 3 10 (ref null (shared any)))
+  (export "size" (func 0))
+  (export "get" (func 1))
+  (export "grow" (func 2))
+  (export "fill" (func 3))
+  (export "copy" (func 4))
+  (export "init" (func 5))
+  (elem (;0;) (i32.const 0) (ref null (shared i31)) (item i32.const 999 ref.i31_shared) (item i32.const 888 ref.i31_shared) (item i32.const 777 ref.i31_shared))
+  (elem $elem (;1;) (ref null (shared i31)) (item i32.const 123 ref.i31_shared) (item i32.const 456 ref.i31_shared) (item i32.const 789 ref.i31_shared))
+)