Repository: bytecodealliance/wasm-tools
Pull request #1664 (2024-07-12 00:32:14)

threads: add remaining instructions
With #1649 merged, we can now implement the remaining shared-everything-threads [instructions]. Many of the tests are generated, so there may be tweaks coming in the future; this change just gets the initial encoding, decoding, and validation pieces in place.

[instructions]: https://github.com/WebAssembly/shared-everything-threads/blob/main/proposals/shared-everything-threads/Overview.md#instructions


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 2d39c9f5..78ef44ca 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -942,9 +942,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
 [[package]]
 name = "log"
-version = "0.4.21"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 
 [[package]]
 name = "logos"
@@ -1302,6 +1302,9 @@ name = "semver"
 version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+dependencies = [
+ "serde",
+]
 
 [[package]]
 name = "serde"
@@ -1699,6 +1702,7 @@ dependencies = [
  "addr2line",
  "anyhow",
  "arbitrary",
+ "bitflags",
  "clap",
  "clap_complete",
  "cpp_demangle",
@@ -2218,6 +2222,7 @@ dependencies = [
  "indoc",
  "pretty_assertions",
  "semver",
+ "serde",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 1ac79b10..5a84f926 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -115,6 +115,7 @@ termcolor = { workspace = true }
 # Dependencies of `validate`
 wasmparser = { workspace = true, optional = true, features = ['validate'] }
 rayon = { workspace = true, optional = true }
+bitflags = { workspace = true, optional = true }
 
 # Dependencies of `print`
 wasmprinter = { workspace = true }
@@ -201,7 +202,7 @@ default = [
 ]
 
 # Each subcommand is gated behind a feature and lists the dependencies it needs
-validate = ['dep:wasmparser', 'rayon', 'dep:addr2line', 'dep:gimli']
+validate = ['dep:wasmparser', 'rayon', 'dep:addr2line', 'dep:gimli', 'dep:bitflags']
 print = []
 parse = []
 smith = ['wasm-smith', 'arbitrary', 'dep:serde', 'dep:serde_derive', 'dep:serde_json']
diff --git a/crates/wasm-encoder/src/core/code.rs b/crates/wasm-encoder/src/core/code.rs
index 12a4eae2..8bfb8fdc 100644
--- a/crates/wasm-encoder/src/core/code.rs
+++ b/crates/wasm-encoder/src/core/code.rs
@@ -1087,6 +1087,122 @@ pub enum Instruction<'a> {
         ordering: Ordering,
         global_index: u32,
     },
+    TableAtomicGet {
+        ordering: Ordering,
+        table_index: u32,
+    },
+    TableAtomicSet {
+        ordering: Ordering,
+        table_index: u32,
+    },
+    TableAtomicRmwXchg {
+        ordering: Ordering,
+        table_index: u32,
+    },
+    TableAtomicRmwCmpxchg {
+        ordering: Ordering,
+        table_index: u32,
+    },
+    StructAtomicGet {
+        ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    },
+    StructAtomicGetS {
+        ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    },
+    StructAtomicGetU {
+        ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    },
+    StructAtomicSet {
+        ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    },
+    StructAtomicRmwAdd {
+        ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    },
+    StructAtomicRmwSub {
+        ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    },
+    StructAtomicRmwAnd {
+        ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    },
+    StructAtomicRmwOr {
+        ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    },
+    StructAtomicRmwXor {
+        ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    },
+    StructAtomicRmwXchg {
+        ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    },
+    StructAtomicRmwCmpxchg {
+        ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    },
+    ArrayAtomicGet {
+        ordering: Ordering,
+        array_type_index: u32,
+    },
+    ArrayAtomicGetS {
+        ordering: Ordering,
+        array_type_index: u32,
+    },
+    ArrayAtomicGetU {
+        ordering: Ordering,
+        array_type_index: u32,
+    },
+    ArrayAtomicSet {
+        ordering: Ordering,
+        array_type_index: u32,
+    },
+    ArrayAtomicRmwAdd {
+        ordering: Ordering,
+        array_type_index: u32,
+    },
+    ArrayAtomicRmwSub {
+        ordering: Ordering,
+        array_type_index: u32,
+    },
+    ArrayAtomicRmwAnd {
+        ordering: Ordering,
+        array_type_index: u32,
+    },
+    ArrayAtomicRmwOr {
+        ordering: Ordering,
+        array_type_index: u32,
+    },
+    ArrayAtomicRmwXor {
+        ordering: Ordering,
+        array_type_index: u32,
+    },
+    ArrayAtomicRmwXchg {
+        ordering: Ordering,
+        array_type_index: u32,
+    },
+    ArrayAtomicRmwCmpxchg {
+        ordering: Ordering,
+        array_type_index: u32,
+    },
+    RefI31Shared,
 }
 
 impl Encode for Instruction<'_> {
@@ -3319,6 +3435,266 @@ impl Encode for Instruction<'_> {
                 ordering.encode(sink);
                 global_index.encode(sink);
             }
+            Instruction::TableAtomicGet {
+                ordering,
+                table_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x58);
+                ordering.encode(sink);
+                table_index.encode(sink);
+            }
+            Instruction::TableAtomicSet {
+                ordering,
+                table_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x59);
+                ordering.encode(sink);
+                table_index.encode(sink);
+            }
+            Instruction::TableAtomicRmwXchg {
+                ordering,
+                table_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x5A);
+                ordering.encode(sink);
+                table_index.encode(sink);
+            }
+            Instruction::TableAtomicRmwCmpxchg {
+                ordering,
+                table_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x5B);
+                ordering.encode(sink);
+                table_index.encode(sink);
+            }
+            Instruction::StructAtomicGet {
+                ordering,
+                struct_type_index,
+                field_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x5C);
+                ordering.encode(sink);
+                struct_type_index.encode(sink);
+                field_index.encode(sink);
+            }
+            Instruction::StructAtomicGetS {
+                ordering,
+                struct_type_index,
+                field_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x5D);
+                ordering.encode(sink);
+                struct_type_index.encode(sink);
+                field_index.encode(sink);
+            }
+            Instruction::StructAtomicGetU {
+                ordering,
+                struct_type_index,
+                field_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x5E);
+                ordering.encode(sink);
+                struct_type_index.encode(sink);
+                field_index.encode(sink);
+            }
+            Instruction::StructAtomicSet {
+                ordering,
+                struct_type_index,
+                field_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x5F);
+                ordering.encode(sink);
+                struct_type_index.encode(sink);
+                field_index.encode(sink);
+            }
+            Instruction::StructAtomicRmwAdd {
+                ordering,
+                struct_type_index,
+                field_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x60);
+                ordering.encode(sink);
+                struct_type_index.encode(sink);
+                field_index.encode(sink);
+            }
+            Instruction::StructAtomicRmwSub {
+                ordering,
+                struct_type_index,
+                field_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x61);
+                ordering.encode(sink);
+                struct_type_index.encode(sink);
+                field_index.encode(sink);
+            }
+            Instruction::StructAtomicRmwAnd {
+                ordering,
+                struct_type_index,
+                field_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x62);
+                ordering.encode(sink);
+                struct_type_index.encode(sink);
+                field_index.encode(sink);
+            }
+            Instruction::StructAtomicRmwOr {
+                ordering,
+                struct_type_index,
+                field_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x63);
+                ordering.encode(sink);
+                struct_type_index.encode(sink);
+                field_index.encode(sink);
+            }
+            Instruction::StructAtomicRmwXor {
+                ordering,
+                struct_type_index,
+                field_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x64);
+                ordering.encode(sink);
+                struct_type_index.encode(sink);
+                field_index.encode(sink);
+            }
+            Instruction::StructAtomicRmwXchg {
+                ordering,
+                struct_type_index,
+                field_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x65);
+                ordering.encode(sink);
+                struct_type_index.encode(sink);
+                field_index.encode(sink);
+            }
+            Instruction::StructAtomicRmwCmpxchg {
+                ordering,
+                struct_type_index,
+                field_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x66);
+                ordering.encode(sink);
+                struct_type_index.encode(sink);
+                field_index.encode(sink);
+            }
+            Instruction::ArrayAtomicGet {
+                ordering,
+                array_type_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x67);
+                ordering.encode(sink);
+                array_type_index.encode(sink);
+            }
+            Instruction::ArrayAtomicGetS {
+                ordering,
+                array_type_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x68);
+                ordering.encode(sink);
+                array_type_index.encode(sink);
+            }
+            Instruction::ArrayAtomicGetU {
+                ordering,
+                array_type_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x69);
+                ordering.encode(sink);
+                array_type_index.encode(sink);
+            }
+            Instruction::ArrayAtomicSet {
+                ordering,
+                array_type_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x6A);
+                ordering.encode(sink);
+                array_type_index.encode(sink);
+            }
+            Instruction::ArrayAtomicRmwAdd {
+                ordering,
+                array_type_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x6B);
+                ordering.encode(sink);
+                array_type_index.encode(sink);
+            }
+            Instruction::ArrayAtomicRmwSub {
+                ordering,
+                array_type_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x6C);
+                ordering.encode(sink);
+                array_type_index.encode(sink);
+            }
+            Instruction::ArrayAtomicRmwAnd {
+                ordering,
+                array_type_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x6D);
+                ordering.encode(sink);
+                array_type_index.encode(sink);
+            }
+            Instruction::ArrayAtomicRmwOr {
+                ordering,
+                array_type_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x6E);
+                ordering.encode(sink);
+                array_type_index.encode(sink);
+            }
+            Instruction::ArrayAtomicRmwXor {
+                ordering,
+                array_type_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x6F);
+                ordering.encode(sink);
+                array_type_index.encode(sink);
+            }
+            Instruction::ArrayAtomicRmwXchg {
+                ordering,
+                array_type_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x70);
+                ordering.encode(sink);
+                array_type_index.encode(sink);
+            }
+            Instruction::ArrayAtomicRmwCmpxchg {
+                ordering,
+                array_type_index,
+            } => {
+                sink.push(0xFE);
+                sink.push(0x71);
+                ordering.encode(sink);
+                array_type_index.encode(sink);
+            }
+            Instruction::RefI31Shared => {
+                sink.push(0xFE);
+                sink.push(0x1F);
+            }
         }
     }
 }
diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs
index 4df00290..3fa9a772 100644
--- a/crates/wasmparser/src/binary_reader.rs
+++ b/crates/wasmparser/src/binary_reader.rs
@@ -688,8 +688,18 @@ impl<'a> BinaryReader<'a> {
         Ok(Ieee64(value))
     }
 
+    /// (internal) Reads a fixed-size WebAssembly string from the module.
+    fn internal_read_string(&mut self, len: usize) -> Result<&'a str> {
+        let bytes = self.read_bytes(len)?;
+        str::from_utf8(bytes).map_err(|_| {
+            BinaryReaderError::new("malformed UTF-8 encoding", self.original_position() - 1)
+        })
+    }
+
     /// Reads a WebAssembly string from the module.
+    ///
     /// # Errors
+    ///
     /// If `BinaryReader` has less than up to four bytes remaining, the string's
     /// length exceeds the remaining bytes, the string's length exceeds
     /// `limits::MAX_WASM_STRING_SIZE`, or the string contains invalid utf-8.
@@ -701,10 +711,17 @@ impl<'a> BinaryReader<'a> {
                 self.original_position() - 1,
             ));
         }
-        let bytes = self.read_bytes(len)?;
-        str::from_utf8(bytes).map_err(|_| {
-            BinaryReaderError::new("malformed UTF-8 encoding", self.original_position() - 1)
-        })
+        return self.internal_read_string(len);
+    }
+
+    /// Reads a unlimited WebAssembly string from the module.
+    ///
+    /// Note that this is similar to [`BinaryReader::read_string`] except that
+    /// it will not limit the size of the returned string by
+    /// `limits::MAX_WASM_STRING_SIZE`.
+    pub fn read_unlimited_string(&mut self) -> Result<&'a str> {
+        let len = self.read_var_u32()? as usize;
+        return self.internal_read_string(len);
     }
 
     #[cold]
@@ -1710,6 +1727,85 @@ impl<'a> BinaryReader<'a> {
             0x57 => {
                 visitor.visit_global_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?)
             }
+            0x58 => visitor.visit_table_atomic_get(self.read_ordering()?, self.read_var_u32()?),
+            0x59 => visitor.visit_table_atomic_set(self.read_ordering()?, self.read_var_u32()?),
+            0x5a => {
+                visitor.visit_table_atomic_rmw_xchg(self.read_ordering()?, self.read_var_u32()?)
+            }
+            0x5b => {
+                visitor.visit_table_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?)
+            }
+            0x5c => visitor.visit_struct_atomic_get(
+                self.read_ordering()?,
+                self.read_var_u32()?,
+                self.read_var_u32()?,
+            ),
+            0x5d => visitor.visit_struct_atomic_get_s(
+                self.read_ordering()?,
+                self.read_var_u32()?,
+                self.read_var_u32()?,
+            ),
+            0x5e => visitor.visit_struct_atomic_get_u(
+                self.read_ordering()?,
+                self.read_var_u32()?,
+                self.read_var_u32()?,
+            ),
+            0x5f => visitor.visit_struct_atomic_set(
+                self.read_ordering()?,
+                self.read_var_u32()?,
+                self.read_var_u32()?,
+            ),
+            0x60 => visitor.visit_struct_atomic_rmw_add(
+                self.read_ordering()?,
+                self.read_var_u32()?,
+                self.read_var_u32()?,
+            ),
+            0x61 => visitor.visit_struct_atomic_rmw_sub(
+                self.read_ordering()?,
+                self.read_var_u32()?,
+                self.read_var_u32()?,
+            ),
+            0x62 => visitor.visit_struct_atomic_rmw_and(
+                self.read_ordering()?,
+                self.read_var_u32()?,
+                self.read_var_u32()?,
+            ),
+            0x63 => visitor.visit_struct_atomic_rmw_or(
+                self.read_ordering()?,
+                self.read_var_u32()?,
+                self.read_var_u32()?,
+            ),
+            0x64 => visitor.visit_struct_atomic_rmw_xor(
+                self.read_ordering()?,
+                self.read_var_u32()?,
+                self.read_var_u32()?,
+            ),
+            0x65 => visitor.visit_struct_atomic_rmw_xchg(
+                self.read_ordering()?,
+                self.read_var_u32()?,
+                self.read_var_u32()?,
+            ),
+            0x66 => visitor.visit_struct_atomic_rmw_cmpxchg(
+                self.read_ordering()?,
+                self.read_var_u32()?,
+                self.read_var_u32()?,
+            ),
+            0x67 => visitor.visit_array_atomic_get(self.read_ordering()?, self.read_var_u32()?),
+            0x68 => visitor.visit_array_atomic_get_s(self.read_ordering()?, self.read_var_u32()?),
+            0x69 => visitor.visit_array_atomic_get_u(self.read_ordering()?, self.read_var_u32()?),
+            0x6a => visitor.visit_array_atomic_set(self.read_ordering()?, self.read_var_u32()?),
+            0x6b => visitor.visit_array_atomic_rmw_add(self.read_ordering()?, self.read_var_u32()?),
+            0x6c => visitor.visit_array_atomic_rmw_sub(self.read_ordering()?, self.read_var_u32()?),
+            0x6d => visitor.visit_array_atomic_rmw_and(self.read_ordering()?, self.read_var_u32()?),
+            0x6e => visitor.visit_array_atomic_rmw_or(self.read_ordering()?, self.read_var_u32()?),
+            0x6f => visitor.visit_array_atomic_rmw_xor(self.read_ordering()?, self.read_var_u32()?),
+            0x70 => {
+                visitor.visit_array_atomic_rmw_xchg(self.read_ordering()?, self.read_var_u32()?)
+            }
+            0x71 => {
+                visitor.visit_array_atomic_rmw_cmpxchg(self.read_ordering()?, self.read_var_u32()?)
+            }
+            0x72 => visitor.visit_ref_i31_shared(),
 
             _ => bail!(pos, "unknown 0xfe subopcode: 0x{code:x}"),
         })
diff --git a/crates/wasmparser/src/features.rs b/crates/wasmparser/src/features.rs
index 6478cf9c..be5e1464 100644
--- a/crates/wasmparser/src/features.rs
+++ b/crates/wasmparser/src/features.rs
@@ -148,6 +148,13 @@ define_wasm_features! {
         pub component_model_nested_names: COMPONENT_MODEL_NESTED_NAMES(1 << 22) = false;
         /// Support for more than 32 flags per-type in the component model.
         pub component_model_more_flags: COMPONENT_MODEL_MORE_FLAGS(1 << 23) = false;
+        /// The WebAssembly legacy exception handling proposal (phase 1)
+        ///
+        /// # Note
+        ///
+        /// Support this feature as long as all leading browsers also support it
+        /// https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/legacy/Exceptions.md
+        pub legacy_exceptions: LEGACY_EXCEPTIONS(1 << 24) = false;
     }
 }
 
diff --git a/crates/wasmparser/src/lib.rs b/crates/wasmparser/src/lib.rs
index 06b1a9ad..0b1c02db 100644
--- a/crates/wasmparser/src/lib.rs
+++ b/crates/wasmparser/src/lib.rs
@@ -158,11 +158,11 @@ macro_rules! for_each_operator {
             @exceptions Throw { tag_index: u32 } => visit_throw
             @exceptions ThrowRef => visit_throw_ref
             // Deprecated old instructions from the exceptions proposal
-            @exceptions Try { blockty: $crate::BlockType } => visit_try
-            @exceptions Catch { tag_index: u32 } => visit_catch
-            @exceptions Rethrow { relative_depth: u32 } => visit_rethrow
-            @exceptions Delegate { relative_depth: u32 } => visit_delegate
-            @exceptions CatchAll => visit_catch_all
+            @legacy_exceptions Try { blockty: $crate::BlockType } => visit_try
+            @legacy_exceptions Catch { tag_index: u32 } => visit_catch
+            @legacy_exceptions Rethrow { relative_depth: u32 } => visit_rethrow
+            @legacy_exceptions Delegate { relative_depth: u32 } => visit_delegate
+            @legacy_exceptions CatchAll => visit_catch_all
             @mvp End => visit_end
             @mvp Br { relative_depth: u32 } => visit_br
             @mvp BrIf { relative_depth: u32 } => visit_br_if
@@ -505,6 +505,33 @@ macro_rules! for_each_operator {
             @shared_everything_threads GlobalAtomicRmwXor { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_rmw_xor
             @shared_everything_threads GlobalAtomicRmwXchg { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_rmw_xchg
             @shared_everything_threads GlobalAtomicRmwCmpxchg { ordering: $crate::Ordering, global_index: u32 } => visit_global_atomic_rmw_cmpxchg
+            @shared_everything_threads TableAtomicGet { ordering: $crate::Ordering, table_index: u32 } => visit_table_atomic_get
+            @shared_everything_threads TableAtomicSet { ordering: $crate::Ordering, table_index: u32 } => visit_table_atomic_set
+            @shared_everything_threads TableAtomicRmwXchg { ordering: $crate::Ordering, table_index: u32 } => visit_table_atomic_rmw_xchg
+            @shared_everything_threads TableAtomicRmwCmpxchg { ordering: $crate::Ordering, table_index: u32 } => visit_table_atomic_rmw_cmpxchg
+            @shared_everything_threads StructAtomicGet { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32  } => visit_struct_atomic_get
+            @shared_everything_threads StructAtomicGetS { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32  } => visit_struct_atomic_get_s
+            @shared_everything_threads StructAtomicGetU { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32  } => visit_struct_atomic_get_u
+            @shared_everything_threads StructAtomicSet { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32  } => visit_struct_atomic_set
+            @shared_everything_threads StructAtomicRmwAdd { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32  } => visit_struct_atomic_rmw_add
+            @shared_everything_threads StructAtomicRmwSub { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32  } => visit_struct_atomic_rmw_sub
+            @shared_everything_threads StructAtomicRmwAnd { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32  } => visit_struct_atomic_rmw_and
+            @shared_everything_threads StructAtomicRmwOr { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32  } => visit_struct_atomic_rmw_or
+            @shared_everything_threads StructAtomicRmwXor { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32  } => visit_struct_atomic_rmw_xor
+            @shared_everything_threads StructAtomicRmwXchg { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32  } => visit_struct_atomic_rmw_xchg
+            @shared_everything_threads StructAtomicRmwCmpxchg { ordering: $crate::Ordering, struct_type_index: u32, field_index: u32  } => visit_struct_atomic_rmw_cmpxchg
+            @shared_everything_threads ArrayAtomicGet { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_get
+            @shared_everything_threads ArrayAtomicGetS { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_get_s
+            @shared_everything_threads ArrayAtomicGetU { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_get_u
+            @shared_everything_threads ArrayAtomicSet { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_set
+            @shared_everything_threads ArrayAtomicRmwAdd { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_add
+            @shared_everything_threads ArrayAtomicRmwSub { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_sub
+            @shared_everything_threads ArrayAtomicRmwAnd { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_and
+            @shared_everything_threads ArrayAtomicRmwOr { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_or
+            @shared_everything_threads ArrayAtomicRmwXor { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_xor
+            @shared_everything_threads ArrayAtomicRmwXchg { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_xchg
+            @shared_everything_threads ArrayAtomicRmwCmpxchg { ordering: $crate::Ordering, array_type_index: u32 } => visit_array_atomic_rmw_cmpxchg
+            @shared_everything_threads RefI31Shared => visit_ref_i31_shared
 
             // 0xFD operators
             // 128-bit SIMD
diff --git a/crates/wasmparser/src/readers/core/names.rs b/crates/wasmparser/src/readers/core/names.rs
index 986e8950..d53211ad 100644
--- a/crates/wasmparser/src/readers/core/names.rs
+++ b/crates/wasmparser/src/readers/core/names.rs
@@ -33,7 +33,10 @@ pub struct Naming<'a> {
 impl<'a> FromReader<'a> for Naming<'a> {
     fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
         let index = reader.read_var_u32()?;
-        let name = reader.read_string()?;
+        // This seems to match what browsers do where they don't limit the
+        // length of names in the `name` section while they do limit the names
+        // in the import and export section for example.
+        let name = reader.read_unlimited_string()?;
         Ok(Naming { index, name })
     }
 }
diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs
index e0f2c7ae..65c44b92 100644
--- a/crates/wasmparser/src/validator/operators.rs
+++ b/crates/wasmparser/src/validator/operators.rs
@@ -28,7 +28,7 @@ use crate::{
     Result, StorageType, StructType, SubType, TableType, TryTable, UnpackedIndex, ValType,
     VisitOperator, WasmFeatures, WasmModuleResources, V128,
 };
-use crate::{prelude::*, CompositeInnerType};
+use crate::{prelude::*, CompositeInnerType, Ordering};
 use core::ops::{Deref, DerefMut};
 
 pub(crate) struct OperatorValidator {
@@ -121,6 +121,24 @@ pub enum FrameKind {
     ///
     /// This belongs to the Wasm exception handling proposal.
     TryTable,
+    /// A Wasm legacy `try` control block.
+    ///
+    /// # Note
+    ///
+    /// See: `WasmFeatures::legacy_exceptions` Note in `crates/wasmparser/src/features.rs`
+    LegacyTry,
+    /// A Wasm legacy `catch` control block.
+    ///
+    /// # Note
+    ///
+    /// See: `WasmFeatures::legacy_exceptions` Note in `crates/wasmparser/src/features.rs`
+    LegacyCatch,
+    /// A Wasm legacy `catch_all` control block.
+    ///
+    /// # Note
+    ///
+    /// See: `WasmFeatures::legacy_exceptions` Note in `crates/wasmparser/src/features.rs`
+    LegacyCatchAll,
 }
 
 struct OperatorValidatorTemp<'validator, 'resources, T> {
@@ -740,17 +758,6 @@ where
         Ok(index_ty)
     }
 
-    /// Validates that the `table` is valid and returns the type it points to.
-    fn check_table_index(&self, table: u32) -> Result<TableType> {
-        match self.resources.table_at(table) {
-            Some(ty) => Ok(ty),
-            None => bail!(
-                self.offset,
-                "unknown table {table}: table index out of bounds"
-            ),
-        }
-    }
-
     fn check_floats_enabled(&self) -> Result<()> {
         if !self.features.floats() {
             bail!(self.offset, "floating-point instruction disallowed");
@@ -874,7 +881,7 @@ where
         type_index: u32,
         table_index: u32,
     ) -> Result<&'resources FuncType> {
-        let tab = self.check_table_index(table_index)?;
+        let tab = self.table_type_at(table_index)?;
         if !self
             .resources
             .is_subtype(ValType::Ref(tab.element_type), ValType::FUNCREF)
@@ -1155,6 +1162,52 @@ where
         Ok(ty)
     }
 
+    /// Common helper for checking the types of structs accessed with atomic RMW
+    /// instructions, which only allow `i32` and `i64` types.
+    fn check_struct_atomic_rmw(
+        &mut self,
+        op: &'static str,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Result<()> {
+        let ty = self
+            .struct_field_at(struct_type_index, field_index)?
+            .element_type;
+        let field_ty = match ty {
+            StorageType::Val(ValType::I32) => ValType::I32,
+            StorageType::Val(ValType::I64) => ValType::I64,
+            _ => bail!(
+                self.offset,
+                "invalid type: `struct.atomic.rmw.{}` only allows `i32` and `i64`",
+                op
+            ),
+        };
+        self.pop_operand(Some(field_ty))?;
+        self.pop_concrete_ref(true, struct_type_index)?;
+        self.push_operand(field_ty)?;
+        Ok(())
+    }
+
+    /// Common helper for checking the types of arrays accessed with atomic RMW
+    /// instructions, which only allow `i32` and `i64`.
+    fn check_array_atomic_rmw(&mut self, op: &'static str, type_index: u32) -> Result<()> {
+        let ty = self.array_type_at(type_index)?.0.element_type;
+        let elem_ty = match ty {
+            StorageType::Val(ValType::I32) => ValType::I32,
+            StorageType::Val(ValType::I64) => ValType::I64,
+            _ => bail!(
+                self.offset,
+                "invalid type: `array.atomic.rmw.{}` only allows `i32` and `i64`",
+                op
+            ),
+        };
+        self.pop_operand(Some(elem_ty))?;
+        self.pop_operand(Some(ValType::I32))?;
+        self.pop_concrete_ref(true, type_index)?;
+        self.push_operand(elem_ty)?;
+        Ok(())
+    }
+
     fn element_type_at(&self, elem_index: u32) -> Result<RefType> {
         match self.resources.element_type_at(elem_index) {
             Some(ty) => Ok(ty),
@@ -1235,6 +1288,17 @@ where
         }
     }
 
+    /// Validates that the `table` is valid and returns the type it points to.
+    fn table_type_at(&self, table: u32) -> Result<TableType> {
+        match self.resources.table_at(table) {
+            Some(ty) => Ok(ty),
+            None => bail!(
+                self.offset,
+                "unknown table {table}: table index out of bounds"
+            ),
+        }
+    }
+
     fn params(&self, ty: BlockType) -> Result<impl PreciseIterator<Item = ValType> + 'resources> {
         Ok(match ty {
             BlockType::Empty | BlockType::Type(_) => Either::B(None.into_iter()),
@@ -1322,6 +1386,7 @@ macro_rules! validate_proposal {
     (desc function_references) => ("function references");
     (desc memory_control) => ("memory control");
     (desc gc) => ("gc");
+    (desc legacy_exceptions) => ("legacy exceptions");
 }
 
 impl<'a, T> VisitOperator<'a> for WasmProposalValidator<'_, '_, T>
@@ -1486,21 +1551,6 @@ where
         self.unreachable()?;
         Ok(())
     }
-    fn visit_try(&mut self, _: BlockType) -> Self::Output {
-        bail!(self.offset, "unimplemented validation of deprecated opcode")
-    }
-    fn visit_catch(&mut self, _: u32) -> Self::Output {
-        bail!(self.offset, "unimplemented validation of deprecated opcode")
-    }
-    fn visit_rethrow(&mut self, _: u32) -> Self::Output {
-        bail!(self.offset, "unimplemented validation of deprecated opcode")
-    }
-    fn visit_delegate(&mut self, _: u32) -> Self::Output {
-        bail!(self.offset, "unimplemented validation of deprecated opcode")
-    }
-    fn visit_catch_all(&mut self) -> Self::Output {
-        bail!(self.offset, "unimplemented validation of deprecated opcode")
-    }
     fn visit_end(&mut self) -> Self::Output {
         let mut frame = self.pop_ctrl()?;
 
@@ -1687,11 +1737,7 @@ where
         self.push_operand(ty)?;
         Ok(())
     }
-    fn visit_global_atomic_get(
-        &mut self,
-        _ordering: crate::Ordering,
-        global_index: u32,
-    ) -> Self::Output {
+    fn visit_global_atomic_get(&mut self, _ordering: Ordering, global_index: u32) -> Self::Output {
         self.visit_global_get(global_index)?;
         // No validation of `ordering` is needed because `global.atomic.get` can
         // be used on both shared and unshared globals. But we do need to limit
@@ -1714,11 +1760,7 @@ where
         self.pop_operand(Some(ty.content_type))?;
         Ok(())
     }
-    fn visit_global_atomic_set(
-        &mut self,
-        _ordering: crate::Ordering,
-        global_index: u32,
-    ) -> Self::Output {
+    fn visit_global_atomic_set(&mut self, _ordering: Ordering, global_index: u32) -> Self::Output {
         self.visit_global_set(global_index)?;
         // No validation of `ordering` is needed because `global.atomic.get` can
         // be used on both shared and unshared globals.
@@ -3612,7 +3654,7 @@ where
         Ok(())
     }
     fn visit_table_init(&mut self, segment: u32, table: u32) -> Self::Output {
-        let table = self.check_table_index(table)?;
+        let table = self.table_type_at(table)?;
         let segment_ty = self.element_type_at(segment)?;
         if !self
             .resources
@@ -3636,8 +3678,8 @@ where
         Ok(())
     }
     fn visit_table_copy(&mut self, dst_table: u32, src_table: u32) -> Self::Output {
-        let src = self.check_table_index(src_table)?;
-        let dst = self.check_table_index(dst_table)?;
+        let src = self.table_type_at(src_table)?;
+        let dst = self.table_type_at(dst_table)?;
         if !self.resources.is_subtype(
             ValType::Ref(src.element_type),
             ValType::Ref(dst.element_type),
@@ -3659,21 +3701,51 @@ where
         Ok(())
     }
     fn visit_table_get(&mut self, table: u32) -> Self::Output {
-        let table = self.check_table_index(table)?;
+        let table = self.table_type_at(table)?;
         debug_assert_type_indices_are_ids(table.element_type.into());
         self.pop_operand(Some(table.index_type()))?;
         self.push_operand(table.element_type)?;
         Ok(())
     }
+    fn visit_table_atomic_get(&mut self, _ordering: Ordering, table: u32) -> Self::Output {
+        self.visit_table_get(table)?;
+        // No validation of `ordering` is needed because `table.atomic.get` can
+        // be used on both shared and unshared tables. But we do need to limit
+        // which types can be used with this instruction.
+        let ty = self.table_type_at(table)?.element_type;
+        let supertype = RefType::ANYREF.shared().unwrap();
+        if !self.resources.is_subtype(ty.into(), supertype.into()) {
+            bail!(
+                self.offset,
+                "invalid type: `table.atomic.get` only allows subtypes of `anyref`"
+            );
+        }
+        Ok(())
+    }
     fn visit_table_set(&mut self, table: u32) -> Self::Output {
-        let table = self.check_table_index(table)?;
+        let table = self.table_type_at(table)?;
         debug_assert_type_indices_are_ids(table.element_type.into());
         self.pop_operand(Some(table.element_type.into()))?;
         self.pop_operand(Some(table.index_type()))?;
         Ok(())
     }
+    fn visit_table_atomic_set(&mut self, _ordering: Ordering, table: u32) -> Self::Output {
+        self.visit_table_set(table)?;
+        // No validation of `ordering` is needed because `table.atomic.set` can
+        // be used on both shared and unshared tables. But we do need to limit
+        // which types can be used with this instruction.
+        let ty = self.table_type_at(table)?.element_type;
+        let supertype = RefType::ANYREF.shared().unwrap();
+        if !self.resources.is_subtype(ty.into(), supertype.into()) {
+            bail!(
+                self.offset,
+                "invalid type: `table.atomic.set` only allows subtypes of `anyref`"
+            );
+        }
+        Ok(())
+    }
     fn visit_table_grow(&mut self, table: u32) -> Self::Output {
-        let table = self.check_table_index(table)?;
+        let table = self.table_type_at(table)?;
         debug_assert_type_indices_are_ids(table.element_type.into());
         self.pop_operand(Some(table.index_type()))?;
         self.pop_operand(Some(table.element_type.into()))?;
@@ -3681,18 +3753,51 @@ where
         Ok(())
     }
     fn visit_table_size(&mut self, table: u32) -> Self::Output {
-        let table = self.check_table_index(table)?;
+        let table = self.table_type_at(table)?;
         self.push_operand(table.index_type())?;
         Ok(())
     }
     fn visit_table_fill(&mut self, table: u32) -> Self::Output {
-        let table = self.check_table_index(table)?;
+        let table = self.table_type_at(table)?;
         debug_assert_type_indices_are_ids(table.element_type.into());
         self.pop_operand(Some(table.index_type()))?;
         self.pop_operand(Some(table.element_type.into()))?;
         self.pop_operand(Some(table.index_type()))?;
         Ok(())
     }
+    fn visit_table_atomic_rmw_xchg(&mut self, _ordering: Ordering, table: u32) -> Self::Output {
+        let table = self.table_type_at(table)?;
+        let elem_ty = table.element_type.into();
+        debug_assert_type_indices_are_ids(elem_ty);
+        let supertype = RefType::ANYREF.shared().unwrap();
+        if !self.resources.is_subtype(elem_ty, supertype.into()) {
+            bail!(
+                self.offset,
+                "invalid type: `table.atomic.rmw.xchg` only allows subtypes of `anyref`"
+            );
+        }
+        self.pop_operand(Some(elem_ty))?;
+        self.pop_operand(Some(table.index_type()))?;
+        self.push_operand(elem_ty)?;
+        Ok(())
+    }
+    fn visit_table_atomic_rmw_cmpxchg(&mut self, _ordering: Ordering, table: u32) -> Self::Output {
+        let table = self.table_type_at(table)?;
+        let elem_ty = table.element_type.into();
+        debug_assert_type_indices_are_ids(elem_ty);
+        let supertype = RefType::EQREF.shared().unwrap();
+        if !self.resources.is_subtype(elem_ty, supertype.into()) {
+            bail!(
+                self.offset,
+                "invalid type: `table.atomic.rmw.cmpxchg` only allows subtypes of `eqref`"
+            );
+        }
+        self.pop_operand(Some(elem_ty))?;
+        self.pop_operand(Some(elem_ty))?;
+        self.pop_operand(Some(table.index_type()))?;
+        self.push_operand(elem_ty)?;
+        Ok(())
+    }
     fn visit_struct_new(&mut self, struct_type_index: u32) -> Self::Output {
         let struct_ty = self.struct_type_at(struct_type_index)?;
         for ty in struct_ty.fields.iter().rev() {
@@ -3726,6 +3831,32 @@ where
         self.pop_concrete_ref(true, struct_type_index)?;
         self.push_operand(field_ty.element_type.unpack())
     }
+    fn visit_struct_atomic_get(
+        &mut self,
+        _ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Self::Output {
+        self.visit_struct_get(struct_type_index, field_index)?;
+        // The `atomic` version has some additional type restrictions.
+        let ty = self
+            .struct_field_at(struct_type_index, field_index)?
+            .element_type;
+        let is_valid_type = match ty {
+            StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
+            StorageType::Val(v) => self
+                .resources
+                .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
+            _ => false,
+        };
+        if !is_valid_type {
+            bail!(
+                self.offset,
+                "invalid type: `struct.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`"
+            );
+        }
+        Ok(())
+    }
     fn visit_struct_get_s(&mut self, struct_type_index: u32, field_index: u32) -> Self::Output {
         let field_ty = self.struct_field_at(struct_type_index, field_index)?;
         if !field_ty.element_type.is_packed() {
@@ -3737,6 +3868,21 @@ where
         self.pop_concrete_ref(true, struct_type_index)?;
         self.push_operand(field_ty.element_type.unpack())
     }
+    fn visit_struct_atomic_get_s(
+        &mut self,
+        _ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Self::Output {
+        self.visit_struct_get_s(struct_type_index, field_index)?;
+        // This instruction has the same type restrictions as the non-`atomic` version.
+        debug_assert!(matches!(
+            self.struct_field_at(struct_type_index, field_index)?
+                .element_type,
+            StorageType::I8 | StorageType::I16
+        ));
+        Ok(())
+    }
     fn visit_struct_get_u(&mut self, struct_type_index: u32, field_index: u32) -> Self::Output {
         let field_ty = self.struct_field_at(struct_type_index, field_index)?;
         if !field_ty.element_type.is_packed() {
@@ -3748,6 +3894,21 @@ where
         self.pop_concrete_ref(true, struct_type_index)?;
         self.push_operand(field_ty.element_type.unpack())
     }
+    fn visit_struct_atomic_get_u(
+        &mut self,
+        _ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Self::Output {
+        self.visit_struct_get_s(struct_type_index, field_index)?;
+        // This instruction has the same type restrictions as the non-`atomic` version.
+        debug_assert!(matches!(
+            self.struct_field_at(struct_type_index, field_index)?
+                .element_type,
+            StorageType::I8 | StorageType::I16
+        ));
+        Ok(())
+    }
     fn visit_struct_set(&mut self, struct_type_index: u32, field_index: u32) -> Self::Output {
         let field_ty = self.struct_field_at(struct_type_index, field_index)?;
         if !field_ty.mutable {
@@ -3757,6 +3918,129 @@ where
         self.pop_concrete_ref(true, struct_type_index)?;
         Ok(())
     }
+    fn visit_struct_atomic_set(
+        &mut self,
+        _ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Self::Output {
+        self.visit_struct_set(struct_type_index, field_index)?;
+        // The `atomic` version has some additional type restrictions.
+        let ty = self
+            .struct_field_at(struct_type_index, field_index)?
+            .element_type;
+        let is_valid_type = match ty {
+            StorageType::I8 | StorageType::I16 => true,
+            StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
+            StorageType::Val(v) => self
+                .resources
+                .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
+        };
+        if !is_valid_type {
+            bail!(
+                self.offset,
+                "invalid type: `struct.atomic.set` only allows `i8`, `i16`, `i32`, `i64` and subtypes of `anyref`"
+            );
+        }
+        Ok(())
+    }
+    fn visit_struct_atomic_rmw_add(
+        &mut self,
+        _ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Self::Output {
+        self.check_struct_atomic_rmw("add", struct_type_index, field_index)
+    }
+    fn visit_struct_atomic_rmw_sub(
+        &mut self,
+        _ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Self::Output {
+        self.check_struct_atomic_rmw("sub", struct_type_index, field_index)
+    }
+    fn visit_struct_atomic_rmw_and(
+        &mut self,
+        _ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Self::Output {
+        self.check_struct_atomic_rmw("and", struct_type_index, field_index)
+    }
+    fn visit_struct_atomic_rmw_or(
+        &mut self,
+        _ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Self::Output {
+        self.check_struct_atomic_rmw("or", struct_type_index, field_index)
+    }
+    fn visit_struct_atomic_rmw_xor(
+        &mut self,
+        _ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Self::Output {
+        self.check_struct_atomic_rmw("xor", struct_type_index, field_index)
+    }
+    fn visit_struct_atomic_rmw_xchg(
+        &mut self,
+        _ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Self::Output {
+        let field_ty = self
+            .struct_field_at(struct_type_index, field_index)?
+            .element_type;
+        let is_valid_type = match field_ty {
+            StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
+            StorageType::Val(v) => self
+                .resources
+                .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
+            _ => false,
+        };
+        if !is_valid_type {
+            bail!(
+                self.offset,
+                "invalid type: `struct.atomic.rmw.xchg` only allows `i32`, `i64` and subtypes of `anyref`"
+            );
+        }
+        let field_ty = field_ty.unpack();
+        self.pop_operand(Some(field_ty))?;
+        self.pop_concrete_ref(true, struct_type_index)?;
+        self.push_operand(field_ty)?;
+        Ok(())
+    }
+    fn visit_struct_atomic_rmw_cmpxchg(
+        &mut self,
+        _ordering: Ordering,
+        struct_type_index: u32,
+        field_index: u32,
+    ) -> Self::Output {
+        let field_ty = self
+            .struct_field_at(struct_type_index, field_index)?
+            .element_type;
+        let is_valid_type = match field_ty {
+            StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
+            StorageType::Val(v) => self
+                .resources
+                .is_subtype(v, RefType::EQREF.shared().unwrap().into()),
+            _ => false,
+        };
+        if !is_valid_type {
+            bail!(
+                self.offset,
+                "invalid type: `struct.atomic.rmw.cmpxchg` only allows `i32`, `i64` and subtypes of `eqref`"
+            );
+        }
+        let field_ty = field_ty.unpack();
+        self.pop_operand(Some(field_ty))?;
+        self.pop_operand(Some(field_ty))?;
+        self.pop_concrete_ref(true, struct_type_index)?;
+        self.push_operand(field_ty)?;
+        Ok(())
+    }
     fn visit_array_new(&mut self, type_index: u32) -> Self::Output {
         let array_ty = self.array_type_at(type_index)?;
         self.pop_operand(Some(ValType::I32))?;
@@ -3839,6 +4123,25 @@ where
         self.pop_concrete_ref(true, type_index)?;
         self.push_operand(elem_ty.unpack())
     }
+    fn visit_array_atomic_get(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
+        self.visit_array_get(type_index)?;
+        // The `atomic` version has some additional type restrictions.
+        let elem_ty = self.array_type_at(type_index)?.0.element_type;
+        let is_valid_type = match elem_ty {
+            StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
+            StorageType::Val(v) => self
+                .resources
+                .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
+            _ => false,
+        };
+        if !is_valid_type {
+            bail!(
+                self.offset,
+                "invalid type: `array.atomic.get` only allows `i32`, `i64` and subtypes of `anyref`"
+            );
+        }
+        Ok(())
+    }
     fn visit_array_get_s(&mut self, type_index: u32) -> Self::Output {
         let array_ty = self.array_type_at(type_index)?;
         let elem_ty = array_ty.0.element_type;
@@ -3852,6 +4155,15 @@ where
         self.pop_concrete_ref(true, type_index)?;
         self.push_operand(elem_ty.unpack())
     }
+    fn visit_array_atomic_get_s(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
+        self.visit_array_get_s(type_index)?;
+        // This instruction has the same type restrictions as the non-`atomic` version.
+        debug_assert!(matches!(
+            self.array_type_at(type_index)?.0.element_type,
+            StorageType::I8 | StorageType::I16
+        ));
+        Ok(())
+    }
     fn visit_array_get_u(&mut self, type_index: u32) -> Self::Output {
         let array_ty = self.array_type_at(type_index)?;
         let elem_ty = array_ty.0.element_type;
@@ -3865,6 +4177,15 @@ where
         self.pop_concrete_ref(true, type_index)?;
         self.push_operand(elem_ty.unpack())
     }
+    fn visit_array_atomic_get_u(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
+        self.visit_array_get_u(type_index)?;
+        // This instruction has the same type restrictions as the non-`atomic` version.
+        debug_assert!(matches!(
+            self.array_type_at(type_index)?.0.element_type,
+            StorageType::I8 | StorageType::I16
+        ));
+        Ok(())
+    }
     fn visit_array_set(&mut self, type_index: u32) -> Self::Output {
         let array_ty = self.array_type_at(type_index)?;
         if !array_ty.0.mutable {
@@ -3875,6 +4196,25 @@ where
         self.pop_concrete_ref(true, type_index)?;
         Ok(())
     }
+    fn visit_array_atomic_set(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
+        self.visit_array_set(type_index)?;
+        // The `atomic` version has some additional type restrictions.
+        let elem_ty = self.array_type_at(type_index)?.0.element_type;
+        let is_valid_type = match elem_ty {
+            StorageType::I8 | StorageType::I16 => true,
+            StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
+            StorageType::Val(v) => self
+                .resources
+                .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
+        };
+        if !is_valid_type {
+            bail!(
+                self.offset,
+                "invalid type: `array.atomic.set` only allows `i8`, `i16`, `i32`, `i64` and subtypes of `anyref`"
+            );
+        }
+        Ok(())
+    }
     fn visit_array_len(&mut self) -> Self::Output {
         self.pop_operand(Some(RefType::ARRAY.nullable().into()))?;
         self.push_operand(ValType::I32)
@@ -3989,6 +4329,74 @@ where
         self.pop_concrete_ref(true, type_index)?;
         Ok(())
     }
+    fn visit_array_atomic_rmw_add(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
+        self.check_array_atomic_rmw("add", type_index)
+    }
+    fn visit_array_atomic_rmw_sub(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
+        self.check_array_atomic_rmw("sub", type_index)
+    }
+    fn visit_array_atomic_rmw_and(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
+        self.check_array_atomic_rmw("and", type_index)
+    }
+    fn visit_array_atomic_rmw_or(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
+        self.check_array_atomic_rmw("or", type_index)
+    }
+    fn visit_array_atomic_rmw_xor(&mut self, _ordering: Ordering, type_index: u32) -> Self::Output {
+        self.check_array_atomic_rmw("xor", type_index)
+    }
+    fn visit_array_atomic_rmw_xchg(
+        &mut self,
+        _ordering: Ordering,
+        type_index: u32,
+    ) -> Self::Output {
+        let elem_ty = self.array_type_at(type_index)?.0.element_type;
+        let is_valid_type = match elem_ty {
+            StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
+            StorageType::Val(v) => self
+                .resources
+                .is_subtype(v, RefType::ANYREF.shared().unwrap().into()),
+            _ => false,
+        };
+        if !is_valid_type {
+            bail!(
+                self.offset,
+                "invalid type: `array.atomic.rmw.xchg` only allows `i32`, `i64` and subtypes of `anyref`"
+            );
+        }
+        let elem_ty = elem_ty.unpack();
+        self.pop_operand(Some(elem_ty))?;
+        self.pop_operand(Some(ValType::I32))?;
+        self.pop_concrete_ref(true, type_index)?;
+        self.push_operand(elem_ty)?;
+        Ok(())
+    }
+    fn visit_array_atomic_rmw_cmpxchg(
+        &mut self,
+        _ordering: Ordering,
+        type_index: u32,
+    ) -> Self::Output {
+        let elem_ty = self.array_type_at(type_index)?.0.element_type;
+        let is_valid_type = match elem_ty {
+            StorageType::Val(ValType::I32) | StorageType::Val(ValType::I64) => true,
+            StorageType::Val(v) => self
+                .resources
+                .is_subtype(v, RefType::EQREF.shared().unwrap().into()),
+            _ => false,
+        };
+        if !is_valid_type {
+            bail!(
+                self.offset,
+                "invalid type: `array.atomic.rmw.cmpxchg` only allows `i32`, `i64` and subtypes of `eqref`"
+            );
+        }
+        let elem_ty = elem_ty.unpack();
+        self.pop_operand(Some(elem_ty))?;
+        self.pop_operand(Some(elem_ty))?;
+        self.pop_operand(Some(ValType::I32))?;
+        self.pop_concrete_ref(true, type_index)?;
+        self.push_operand(elem_ty)?;
+        Ok(())
+    }
     fn visit_any_convert_extern(&mut self) -> Self::Output {
         let extern_ref = self.pop_operand(Some(RefType::EXTERNREF.into()))?;
         let is_nullable = extern_ref
@@ -4060,7 +4468,7 @@ where
             ),
             None => bail!(
                 self.offset,
-                "type mismtach: br_on_cast to label with empty types, must have a reference type"
+                "type mismatch: br_on_cast to label with empty types, must have a reference type"
             ),
         };
 
@@ -4116,6 +4524,10 @@ where
         self.pop_operand(Some(ValType::I32))?;
         self.push_operand(ValType::Ref(RefType::I31))
     }
+    fn visit_ref_i31_shared(&mut self) -> Self::Output {
+        self.pop_operand(Some(ValType::I32))?;
+        self.push_operand(ValType::Ref(RefType::I31)) // TODO: handle shared--is this correct?
+    }
     fn visit_i31_get_s(&mut self) -> Self::Output {
         self.pop_operand(Some(ValType::Ref(RefType::I31REF)))?;
         self.push_operand(ValType::I32)
@@ -4124,6 +4536,80 @@ where
         self.pop_operand(Some(ValType::Ref(RefType::I31REF)))?;
         self.push_operand(ValType::I32)
     }
+    fn visit_try(&mut self, mut ty: BlockType) -> Self::Output {
+        self.check_block_type(&mut ty)?;
+        for ty in self.params(ty)?.rev() {
+            self.pop_operand(Some(ty))?;
+        }
+        self.push_ctrl(FrameKind::LegacyTry, ty)?;
+        Ok(())
+    }
+    fn visit_catch(&mut self, index: u32) -> Self::Output {
+        let frame = self.pop_ctrl()?;
+        if frame.kind != FrameKind::LegacyTry && frame.kind != FrameKind::LegacyCatch {
+            bail!(self.offset, "catch found outside of an `try` block");
+        }
+        // Start a new frame and push `exnref` value.
+        let height = self.operands.len();
+        let init_height = self.inits.len();
+        self.control.push(Frame {
+            kind: FrameKind::LegacyCatch,
+            block_type: frame.block_type,
+            height,
+            unreachable: false,
+            init_height,
+        });
+        // Push exception argument types.
+        let ty = self.tag_at(index)?;
+        for ty in ty.params() {
+            self.push_operand(*ty)?;
+        }
+        Ok(())
+    }
+    fn visit_rethrow(&mut self, relative_depth: u32) -> Self::Output {
+        // This is not a jump, but we need to check that the `rethrow`
+        // targets an actual `catch` to get the exception.
+        let (_, kind) = self.jump(relative_depth)?;
+        if kind != FrameKind::LegacyCatch && kind != FrameKind::LegacyCatchAll {
+            bail!(
+                self.offset,
+                "invalid rethrow label: target was not a `catch` block"
+            );
+        }
+        self.unreachable()?;
+        Ok(())
+    }
+    fn visit_delegate(&mut self, relative_depth: u32) -> Self::Output {
+        let frame = self.pop_ctrl()?;
+        if frame.kind != FrameKind::LegacyTry {
+            bail!(self.offset, "delegate found outside of an `try` block");
+        }
+        // This operation is not a jump, but we need to check the
+        // depth for validity
+        let _ = self.jump(relative_depth)?;
+        for ty in self.results(frame.block_type)? {
+            self.push_operand(ty)?;
+        }
+        Ok(())
+    }
+    fn visit_catch_all(&mut self) -> Self::Output {
+        let frame = self.pop_ctrl()?;
+        if frame.kind == FrameKind::LegacyCatchAll {
+            bail!(self.offset, "only one catch_all allowed per `try` block");
+        } else if frame.kind != FrameKind::LegacyTry && frame.kind != FrameKind::LegacyCatch {
+            bail!(self.offset, "catch_all found outside of a `try` block");
+        }
+        let height = self.operands.len();
+        let init_height = self.inits.len();
+        self.control.push(Frame {
+            kind: FrameKind::LegacyCatchAll,
+            block_type: frame.block_type,
+            height,
+            unreachable: false,
+            init_height,
+        });
+        Ok(())
+    }
 }
 
 #[derive(Clone, Debug)]
diff --git a/crates/wasmprinter/src/operator.rs b/crates/wasmprinter/src/operator.rs
index fa7e2ca4..558bdc86 100644
--- a/crates/wasmprinter/src/operator.rs
+++ b/crates/wasmprinter/src/operator.rs
@@ -595,6 +595,78 @@ macro_rules! define_visit {
         $self.push_str(" ")?;
         $self.printer.print_field_idx($self.state, $ty, $field)?;
     );
+    (payload $self:ident StructAtomicGet $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
+    (payload $self:ident StructAtomicGetS $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
+    (payload $self:ident StructAtomicGetU $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
+    (payload $self:ident StructAtomicSet $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
+    (payload $self:ident StructAtomicSet $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
+    (payload $self:ident StructAtomicRmwAdd $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
+    (payload $self:ident StructAtomicRmwSub $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
+    (payload $self:ident StructAtomicRmwAnd $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
+    (payload $self:ident StructAtomicRmwOr $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
+    (payload $self:ident StructAtomicRmwXor $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
+    (payload $self:ident StructAtomicRmwXchg $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
+    (payload $self:ident StructAtomicRmwCmpxchg $order:ident $ty:ident $field:ident) => (
+        $self.ordering($order)?;
+        $self.struct_type_index($ty)?;
+        $self.push_str(" ")?;
+        $self.printer.print_field_idx($self.state, $ty, $field)?;
+    );
     (payload $self:ident $op:ident $($arg:ident)*) => (
         $($self.$arg($arg)?;)*
     );
@@ -1180,6 +1252,33 @@ macro_rules! define_visit {
     (name GlobalAtomicRmwXor) => ("global.atomic.rmw.xor");
     (name GlobalAtomicRmwXchg) => ("global.atomic.rmw.xchg");
     (name GlobalAtomicRmwCmpxchg) => ("global.atomic.rmw.cmpxchg");
+    (name TableAtomicGet) => ("table.atomic.get");
+    (name TableAtomicSet) => ("table.atomic.set");
+    (name TableAtomicRmwXchg) => ("table.atomic.rmw.xchg");
+    (name TableAtomicRmwCmpxchg) => ("table.atomic.rmw.cmpxchg");
+    (name StructAtomicGet) => ("struct.atomic.get");
+    (name StructAtomicGetS) => ("struct.atomic.get_s");
+    (name StructAtomicGetU) => ("struct.atomic.get_u");
+    (name StructAtomicSet) => ("struct.atomic.set");
+    (name StructAtomicRmwAdd) => ("struct.atomic.rmw.add");
+    (name StructAtomicRmwSub) => ("struct.atomic.rmw.sub");
+    (name StructAtomicRmwAnd) => ("struct.atomic.rmw.and");
+    (name StructAtomicRmwOr) => ("struct.atomic.rmw.or");
+    (name StructAtomicRmwXor) => ("struct.atomic.rmw.xor");
+    (name StructAtomicRmwXchg) => ("struct.atomic.rmw.xchg");
+    (name StructAtomicRmwCmpxchg) => ("struct.atomic.rmw.cmpxchg");
+    (name ArrayAtomicGet) => ("array.atomic.get");
+    (name ArrayAtomicGetS) => ("array.atomic.get_s");
+    (name ArrayAtomicGetU) => ("array.atomic.get_u");
+    (name ArrayAtomicSet) => ("array.atomic.set");
+    (name ArrayAtomicRmwAdd) => ("array.atomic.rmw.add");
+    (name ArrayAtomicRmwSub) => ("array.atomic.rmw.sub");
+    (name ArrayAtomicRmwAnd) => ("array.atomic.rmw.and");
+    (name ArrayAtomicRmwOr) => ("array.atomic.rmw.or");
+    (name ArrayAtomicRmwXor) => ("array.atomic.rmw.xor");
+    (name ArrayAtomicRmwXchg) => ("array.atomic.rmw.xchg");
+    (name ArrayAtomicRmwCmpxchg) => ("array.atomic.rmw.cmpxchg");
+    (name RefI31Shared) => ("ref.i31_shared")
 }
 
 impl<'a> VisitOperator<'a> for PrintOperator<'_, '_, '_, '_> {
diff --git a/crates/wast/src/core/binary.rs b/crates/wast/src/core/binary.rs
index 47fd4b0d..5007c425 100644
--- a/crates/wast/src/core/binary.rs
+++ b/crates/wast/src/core/binary.rs
@@ -928,10 +928,13 @@ impl Encode for Ordering {
     }
 }
 
-impl Encode for OrderedAccess<'_> {
+impl<T> Encode for Ordered<T>
+where
+    T: Encode,
+{
     fn encode(&self, buf: &mut Vec<u8>) {
         self.ordering.encode(buf);
-        self.index.encode(buf);
+        self.inner.encode(buf);
     }
 }
 
diff --git a/crates/wast/src/core/expr.rs b/crates/wast/src/core/expr.rs
index 9104a8ea..c4520310 100644
--- a/crates/wast/src/core/expr.rs
+++ b/crates/wast/src/core/expr.rs
@@ -859,15 +859,42 @@ instructions! {
         I64AtomicRmw32CmpxchgU(MemArg<4>) : [0xfe, 0x4e] : "i64.atomic.rmw32.cmpxchg_u",
 
         // proposal: shared-everything-threads
-        GlobalAtomicGet(OrderedAccess<'a>) : [0xfe, 0x4f] : "global.atomic.get",
-        GlobalAtomicSet(OrderedAccess<'a>) : [0xfe, 0x50] : "global.atomic.set",
-        GlobalAtomicRmwAdd(OrderedAccess<'a>) : [0xfe, 0x51] : "global.atomic.rmw.add",
-        GlobalAtomicRmwSub(OrderedAccess<'a>) : [0xfe, 0x52] : "global.atomic.rmw.sub",
-        GlobalAtomicRmwAnd(OrderedAccess<'a>) : [0xfe, 0x53] : "global.atomic.rmw.and",
-        GlobalAtomicRmwOr(OrderedAccess<'a>) : [0xfe, 0x54] : "global.atomic.rmw.or",
-        GlobalAtomicRmwXor(OrderedAccess<'a>) : [0xfe, 0x55] : "global.atomic.rmw.xor",
-        GlobalAtomicRmwXchg(OrderedAccess<'a>) : [0xfe, 0x56] : "global.atomic.rmw.xchg",
-        GlobalAtomicRmwCmpxchg(OrderedAccess<'a>) : [0xfe, 0x57] : "global.atomic.rmw.cmpxchg",
+        GlobalAtomicGet(Ordered<Index<'a>>) : [0xfe, 0x4f] : "global.atomic.get",
+        GlobalAtomicSet(Ordered<Index<'a>>) : [0xfe, 0x50] : "global.atomic.set",
+        GlobalAtomicRmwAdd(Ordered<Index<'a>>) : [0xfe, 0x51] : "global.atomic.rmw.add",
+        GlobalAtomicRmwSub(Ordered<Index<'a>>) : [0xfe, 0x52] : "global.atomic.rmw.sub",
+        GlobalAtomicRmwAnd(Ordered<Index<'a>>) : [0xfe, 0x53] : "global.atomic.rmw.and",
+        GlobalAtomicRmwOr(Ordered<Index<'a>>) : [0xfe, 0x54] : "global.atomic.rmw.or",
+        GlobalAtomicRmwXor(Ordered<Index<'a>>) : [0xfe, 0x55] : "global.atomic.rmw.xor",
+        GlobalAtomicRmwXchg(Ordered<Index<'a>>) : [0xfe, 0x56] : "global.atomic.rmw.xchg",
+        GlobalAtomicRmwCmpxchg(Ordered<Index<'a>>) : [0xfe, 0x57] : "global.atomic.rmw.cmpxchg",
+        TableAtomicGet(Ordered<TableArg<'a>>) : [0xfe, 0x58] : "table.atomic.get",
+        TableAtomicSet(Ordered<TableArg<'a>>) : [0xfe, 0x59] : "table.atomic.set",
+        TableAtomicRmwXchg(Ordered<TableArg<'a>>) : [0xfe, 0x5A] : "table.atomic.rmw.xchg",
+        TableAtomicRmwCmpxchg(Ordered<TableArg<'a>>) : [0xFE, 0x5B] : "table.atomic.rmw.cmpxchg",
+        StructAtomicGet(Ordered<StructAccess<'a>>) : [0xFE, 0x5C] : "struct.atomic.get",
+        StructAtomicGetS(Ordered<StructAccess<'a>>) : [0xFE, 0x5D] : "struct.atomic.get_s",
+        StructAtomicGetU(Ordered<StructAccess<'a>>) : [0xFE, 0x5E] : "struct.atomic.get_u",
+        StructAtomicSet(Ordered<StructAccess<'a>>) : [0xFE, 0x5F] : "struct.atomic.set",
+        StructAtomicRmwAdd(Ordered<StructAccess<'a>>) : [0xFE, 0x60] : "struct.atomic.rmw.add",
+        StructAtomicRmwSub(Ordered<StructAccess<'a>>) : [0xFE, 0x61] : "struct.atomic.rmw.sub",
+        StructAtomicRmwAnd(Ordered<StructAccess<'a>>) : [0xFE, 0x62] : "struct.atomic.rmw.and",
+        StructAtomicRmwOr(Ordered<StructAccess<'a>>) : [0xFE, 0x63] : "struct.atomic.rmw.or",
+        StructAtomicRmwXor(Ordered<StructAccess<'a>>) : [0xFE, 0x64] : "struct.atomic.rmw.xor",
+        StructAtomicRmwXchg(Ordered<StructAccess<'a>>) : [0xFE, 0x65] : "struct.atomic.rmw.xchg",
+        StructAtomicRmwCmpxchg(Ordered<StructAccess<'a>>) : [0xFE, 0x66] : "struct.atomic.rmw.cmpxchg",
+        ArrayAtomicGet(Ordered<Index<'a>>) : [0xFE, 0x67] : "array.atomic.get",
+        ArrayAtomicGetS(Ordered<Index<'a>>) : [0xFE, 0x68] : "array.atomic.get_s",
+        ArrayAtomicGetU(Ordered<Index<'a>>) : [0xFE, 0x69] : "array.atomic.get_u",
+        ArrayAtomicSet(Ordered<Index<'a>>) : [0xFE, 0x6A] : "array.atomic.set",
+        ArrayAtomicRmwAdd(Ordered<Index<'a>>) : [0xFE, 0x6B] : "array.atomic.rmw.add",
+        ArrayAtomicRmwSub(Ordered<Index<'a>>) : [0xFE, 0x6C] : "array.atomic.rmw.sub",
+        ArrayAtomicRmwAnd(Ordered<Index<'a>>) : [0xFE, 0x6D] : "array.atomic.rmw.and",
+        ArrayAtomicRmwOr(Ordered<Index<'a>>) : [0xFE, 0x6E] : "array.atomic.rmw.or",
+        ArrayAtomicRmwXor(Ordered<Index<'a>>) : [0xFE, 0x6F] : "array.atomic.rmw.xor",
+        ArrayAtomicRmwXchg(Ordered<Index<'a>>) : [0xFE, 0x70] : "array.atomic.rmw.xchg",
+        ArrayAtomicRmwCmpxchg(Ordered<Index<'a>>) : [0xFE, 0x71] : "array.atomic.rmw.cmpxchg",
+        RefI31Shared : [0xFE, 0x72] : "ref.i31_shared",
 
         // proposal: simd
         //
@@ -1531,6 +1558,8 @@ pub struct TableArg<'a> {
     pub dst: Index<'a>,
 }
 
+// `TableArg` could be an unwrapped as an `Index` if not for this custom parse
+// behavior: if we cannot parse a table index, we default to table `0`.
 impl<'a> Parse<'a> for TableArg<'a> {
     fn parse(parser: Parser<'a>) -> Result<Self> {
         let dst = if let Some(dst) = parser.parse()? {
@@ -1831,20 +1860,27 @@ impl<'a> Parse<'a> for Ordering {
     }
 }
 
-/// Extra data associated with the `global.atomic.*` instructions.
+/// Add a memory [`Ordering`] to the argument `T` of some instruction.
+///
+/// This is helpful for many kinds of `*.atomic.*` instructions introduced by
+/// the shared-everything-threads proposal. Many of these instructions "build
+/// on" existing instructions by simply adding a memory order to them.
 #[derive(Clone, Debug)]
-pub struct OrderedAccess<'a> {
+pub struct Ordered<T> {
     /// The memory ordering for this atomic instruction.
     pub ordering: Ordering,
-    /// The index of the global to access.
-    pub index: Index<'a>,
+    /// The original argument type.
+    pub inner: T,
 }
 
-impl<'a> Parse<'a> for OrderedAccess<'a> {
+impl<'a, T> Parse<'a> for Ordered<T>
+where
+    T: Parse<'a>,
+{
     fn parse(parser: Parser<'a>) -> Result<Self> {
         let ordering = parser.parse()?;
-        let index = parser.parse()?;
-        Ok(OrderedAccess { ordering, index })
+        let inner = parser.parse()?;
+        Ok(Ordered { ordering, inner })
     }
 }
 
diff --git a/crates/wast/src/core/resolve/names.rs b/crates/wast/src/core/resolve/names.rs
index d2b7eb1d..c0c5e5b5 100644
--- a/crates/wast/src/core/resolve/names.rs
+++ b/crates/wast/src/core/resolve/names.rs
@@ -453,6 +453,13 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
                 self.resolver.resolve(&mut i.dst, Ns::Table)?;
             }
 
+            TableAtomicGet(i)
+            | TableAtomicSet(i)
+            | TableAtomicRmwXchg(i)
+            | TableAtomicRmwCmpxchg(i) => {
+                self.resolver.resolve(&mut i.inner.dst, Ns::Table)?;
+            }
+
             GlobalSet(i) | GlobalGet(i) => {
                 self.resolver.resolve(i, Ns::Global)?;
             }
@@ -466,7 +473,7 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
             | GlobalAtomicRmwXor(i)
             | GlobalAtomicRmwXchg(i)
             | GlobalAtomicRmwCmpxchg(i) => {
-                self.resolver.resolve(&mut i.index, Ns::Global)?;
+                self.resolver.resolve(&mut i.inner, Ns::Global)?;
             }
 
             LocalSet(i) | LocalGet(i) | LocalTee(i) => {
@@ -614,14 +621,21 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
             }
 
             StructSet(s) | StructGet(s) | StructGetS(s) | StructGetU(s) => {
-                let type_index = self.resolver.resolve(&mut s.r#struct, Ns::Type)?;
-                if let Index::Id(field_id) = s.field {
-                    self.resolver
-                        .fields
-                        .get(&type_index)
-                        .ok_or(Error::new(field_id.span(), format!("accessing a named field `{}` in a struct without named fields, type index {}", field_id.name(), type_index)))?
-                        .resolve(&mut s.field, "field")?;
-                }
+                self.resolve_field(s)?;
+            }
+
+            StructAtomicGet(s)
+            | StructAtomicGetS(s)
+            | StructAtomicGetU(s)
+            | StructAtomicSet(s)
+            | StructAtomicRmwAdd(s)
+            | StructAtomicRmwSub(s)
+            | StructAtomicRmwAnd(s)
+            | StructAtomicRmwOr(s)
+            | StructAtomicRmwXor(s)
+            | StructAtomicRmwXchg(s)
+            | StructAtomicRmwCmpxchg(s) => {
+                self.resolve_field(&mut s.inner)?;
             }
 
             ArrayNewFixed(a) => {
@@ -651,6 +665,20 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
                 self.resolver.elems.resolve(&mut a.segment, "elem")?;
             }
 
+            ArrayAtomicGet(i)
+            | ArrayAtomicGetS(i)
+            | ArrayAtomicGetU(i)
+            | ArrayAtomicSet(i)
+            | ArrayAtomicRmwAdd(i)
+            | ArrayAtomicRmwSub(i)
+            | ArrayAtomicRmwAnd(i)
+            | ArrayAtomicRmwOr(i)
+            | ArrayAtomicRmwXor(i)
+            | ArrayAtomicRmwXchg(i)
+            | ArrayAtomicRmwCmpxchg(i) => {
+                self.resolver.resolve(&mut i.inner, Ns::Type)?;
+            }
+
             RefNull(ty) => self.resolver.resolve_heaptype(ty)?,
 
             _ => {}
@@ -678,6 +706,18 @@ impl<'a, 'b> ExprResolver<'a, 'b> {
             None => Err(resolve_error(id, "label")),
         }
     }
+
+    fn resolve_field(&self, s: &mut StructAccess<'a>) -> Result<(), Error> {
+        let type_index = self.resolver.resolve(&mut s.r#struct, Ns::Type)?;
+        if let Index::Id(field_id) = s.field {
+            self.resolver
+                        .fields
+                        .get(&type_index)
+                        .ok_or(Error::new(field_id.span(), format!("accessing a named field `{}` in a struct without named fields, type index {}", field_id.name(), type_index)))?
+                        .resolve(&mut s.field, "field")?;
+        }
+        Ok(())
+    }
 }
 
 enum TypeInfo<'a> {
diff --git a/crates/wit-encoder/Cargo.toml b/crates/wit-encoder/Cargo.toml
index 00f80ba3..cc72b4c7 100644
--- a/crates/wit-encoder/Cargo.toml
+++ b/crates/wit-encoder/Cargo.toml
@@ -10,9 +10,24 @@ version.workspace = true
 [lints]
 workspace = true
 
+[features]
+default = ["serde"]
+
+# Enables JSON serialization/deserialization of the wit-encoder structures.
+
+# *Note*: The JSON that this generates is different from the JSON generated from wit-parser.
+# If you're looking to create WIT from JSON, then this is the crate and feature for you. But if
+# you're parsing WIT and reading the output through JSON, then wit-parser is probably the better
+# option.
+
+# *Note*: The exact structure of the JSON is likely not going to be very stable over time,
+# so slight tweaks and variants should be expected as this crate evolves.
+serde = ["dep:serde", "semver/serde"]
+
 [dependencies]
 semver = { workspace = true }
 pretty_assertions = { workspace = true }
+serde = { workspace = true, optional = true, features = ["derive"] }
 
 [dev-dependencies]
 indoc = { workspace = true }
diff --git a/crates/wit-encoder/src/docs.rs b/crates/wit-encoder/src/docs.rs
index 137fd744..86fe2059 100644
--- a/crates/wit-encoder/src/docs.rs
+++ b/crates/wit-encoder/src/docs.rs
@@ -4,6 +4,8 @@ use crate::{Render, RenderOpts};
 
 /// Documentation
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Docs {
     contents: String,
 }
diff --git a/crates/wit-encoder/src/enum_.rs b/crates/wit-encoder/src/enum_.rs
index b84f3725..73f17eb3 100644
--- a/crates/wit-encoder/src/enum_.rs
+++ b/crates/wit-encoder/src/enum_.rs
@@ -2,6 +2,8 @@ use crate::{Docs, Ident};
 
 /// A variant without a payload
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Enum {
     pub(crate) cases: Vec<EnumCase>,
 }
@@ -36,6 +38,8 @@ impl Enum {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct EnumCase {
     pub(crate) name: Ident,
     pub(crate) docs: Option<Docs>,
diff --git a/crates/wit-encoder/src/flags.rs b/crates/wit-encoder/src/flags.rs
index 81e2fd0f..69735cbb 100644
--- a/crates/wit-encoder/src/flags.rs
+++ b/crates/wit-encoder/src/flags.rs
@@ -1,6 +1,8 @@
 use crate::{ident::Ident, Docs};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Flags {
     pub(crate) flags: Vec<Flag>,
 }
@@ -22,6 +24,8 @@ impl Flags {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Flag {
     pub(crate) name: Ident,
     pub(crate) docs: Option<Docs>,
diff --git a/crates/wit-encoder/src/function.rs b/crates/wit-encoder/src/function.rs
index 6c86aa26..d00a8752 100644
--- a/crates/wit-encoder/src/function.rs
+++ b/crates/wit-encoder/src/function.rs
@@ -3,6 +3,8 @@ use std::fmt::{self, Display};
 use crate::{ident::Ident, Docs, Type};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Params {
     items: Vec<(Ident, Type)>,
 }
@@ -61,6 +63,8 @@ impl Params {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub enum Results {
     Named(Params),
     Anon(Type),
@@ -141,6 +145,8 @@ impl Results {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct StandaloneFunc {
     pub(crate) name: Ident,
     pub(crate) params: Params,
diff --git a/crates/wit-encoder/src/ident.rs b/crates/wit-encoder/src/ident.rs
index c336173c..d0a29627 100644
--- a/crates/wit-encoder/src/ident.rs
+++ b/crates/wit-encoder/src/ident.rs
@@ -1,6 +1,8 @@
 use std::{borrow::Cow, fmt};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Ident(Cow<'static, str>);
 
 impl Ident {
diff --git a/crates/wit-encoder/src/include.rs b/crates/wit-encoder/src/include.rs
index 9f276381..f9a5cd93 100644
--- a/crates/wit-encoder/src/include.rs
+++ b/crates/wit-encoder/src/include.rs
@@ -4,6 +4,8 @@ use crate::{Ident, Render};
 
 /// Enable the union of a world with another world
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Include {
     use_path: Ident,
     include_names_list: Vec<(String, String)>,
diff --git a/crates/wit-encoder/src/interface.rs b/crates/wit-encoder/src/interface.rs
index bf082716..a4b87611 100644
--- a/crates/wit-encoder/src/interface.rs
+++ b/crates/wit-encoder/src/interface.rs
@@ -3,6 +3,8 @@ use std::fmt;
 use crate::{Docs, Ident, Render, RenderOpts, StandaloneFunc, TypeDef, Use};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Interface {
     /// Name of this interface.
     pub(crate) name: Ident,
@@ -54,6 +56,8 @@ impl Interface {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub enum InterfaceItem {
     TypeDef(TypeDef),
     Use(Use),
diff --git a/crates/wit-encoder/src/package.rs b/crates/wit-encoder/src/package.rs
index 0289e052..74fa9623 100644
--- a/crates/wit-encoder/src/package.rs
+++ b/crates/wit-encoder/src/package.rs
@@ -10,6 +10,8 @@ use crate::{ident::Ident, Interface, Render, RenderOpts, World};
 /// have a unique identifier that affects generated components and uniquely
 /// identifiers this particular package.
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Package {
     /// A unique name corresponding to this package.
     name: PackageName,
@@ -81,6 +83,8 @@ impl fmt::Display for Package {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub enum PackageItem {
     Interface(Interface),
     World(World),
@@ -92,6 +96,8 @@ pub enum PackageItem {
 /// This is directly encoded as an "ID" in the binary component representation
 /// with an interfaced tacked on as well.
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct PackageName {
     /// A namespace such as `wasi` in `wasi:foo/bar`
     namespace: String,
diff --git a/crates/wit-encoder/src/record.rs b/crates/wit-encoder/src/record.rs
index 9f91a8cd..35c7f677 100644
--- a/crates/wit-encoder/src/record.rs
+++ b/crates/wit-encoder/src/record.rs
@@ -1,6 +1,8 @@
 use crate::{ident::Ident, Docs, Type};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Record {
     pub(crate) fields: Vec<Field>,
 }
@@ -22,6 +24,8 @@ impl Record {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Field {
     pub(crate) name: Ident,
     pub(crate) ty: Type,
diff --git a/crates/wit-encoder/src/render.rs b/crates/wit-encoder/src/render.rs
index 62cda3c8..60a710f9 100644
--- a/crates/wit-encoder/src/render.rs
+++ b/crates/wit-encoder/src/render.rs
@@ -1,6 +1,8 @@
 use std::fmt;
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct RenderOpts {
     /// width of each indent
     pub indent_width: usize,
diff --git a/crates/wit-encoder/src/resource.rs b/crates/wit-encoder/src/resource.rs
index 644cca97..85d51714 100644
--- a/crates/wit-encoder/src/resource.rs
+++ b/crates/wit-encoder/src/resource.rs
@@ -1,6 +1,8 @@
 use crate::{ident::Ident, Docs, Params, Results};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Resource {
     pub(crate) funcs: Vec<ResourceFunc>,
 }
@@ -24,6 +26,8 @@ impl Resource {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct ResourceFunc {
     pub(crate) kind: ResourceFuncKind,
     pub(crate) params: Params,
@@ -31,6 +35,8 @@ pub struct ResourceFunc {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub enum ResourceFuncKind {
     Method(Ident, Results),
     Static(Ident, Results),
diff --git a/crates/wit-encoder/src/result.rs b/crates/wit-encoder/src/result.rs
index de4dc9cb..ee8ff119 100644
--- a/crates/wit-encoder/src/result.rs
+++ b/crates/wit-encoder/src/result.rs
@@ -3,6 +3,8 @@ use std::fmt::Display;
 use crate::Type;
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Result_ {
     ok: Option<Type>,
     err: Option<Type>,
diff --git a/crates/wit-encoder/src/tuple.rs b/crates/wit-encoder/src/tuple.rs
index 0bc97bac..ee3c9a4c 100644
--- a/crates/wit-encoder/src/tuple.rs
+++ b/crates/wit-encoder/src/tuple.rs
@@ -3,6 +3,8 @@ use std::fmt::Display;
 use crate::Type;
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Tuple {
     pub(crate) types: Vec<Type>,
 }
diff --git a/crates/wit-encoder/src/ty.rs b/crates/wit-encoder/src/ty.rs
index 3e45f6d6..95b72682 100644
--- a/crates/wit-encoder/src/ty.rs
+++ b/crates/wit-encoder/src/ty.rs
@@ -6,6 +6,8 @@ use crate::{
 };
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub enum Type {
     Bool,
     U8,
@@ -111,6 +113,8 @@ impl Display for Type {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct VariantCase {
     name: Ident,
     ty: Option<Type>,
@@ -176,6 +180,8 @@ where
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct TypeDef {
     name: Ident,
     kind: TypeDefKind,
@@ -273,6 +279,8 @@ impl TypeDef {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub enum TypeDefKind {
     Record(Record),
     Resource(Resource),
diff --git a/crates/wit-encoder/src/use_.rs b/crates/wit-encoder/src/use_.rs
index 9322c15d..bb549adb 100644
--- a/crates/wit-encoder/src/use_.rs
+++ b/crates/wit-encoder/src/use_.rs
@@ -4,6 +4,8 @@ use crate::{Ident, Render};
 
 /// Enable the union of a world with another world
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Use {
     target: Ident,
     use_names_list: Vec<(String, Option<String>)>,
diff --git a/crates/wit-encoder/src/variant.rs b/crates/wit-encoder/src/variant.rs
index 2322f9da..43bdf676 100644
--- a/crates/wit-encoder/src/variant.rs
+++ b/crates/wit-encoder/src/variant.rs
@@ -1,6 +1,8 @@
 use crate::VariantCase;
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct Variant {
     pub(crate) cases: Vec<VariantCase>,
 }
diff --git a/crates/wit-encoder/src/world.rs b/crates/wit-encoder/src/world.rs
index 5f933209..d380f62b 100644
--- a/crates/wit-encoder/src/world.rs
+++ b/crates/wit-encoder/src/world.rs
@@ -3,6 +3,8 @@ use std::fmt;
 use crate::{ident::Ident, Docs, Include, Interface, Render, RenderOpts, StandaloneFunc, Use};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct World {
     /// The WIT identifier name of this world.
     name: Ident,
@@ -156,6 +158,8 @@ impl Render for World {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub enum WorldItem {
     /// An imported inline interface
     InlineInterfaceImport(Interface),
@@ -207,6 +211,8 @@ impl WorldItem {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
 pub struct WorldNamedInterface {
     /// Name of this interface.
     pub(crate) name: Ident,
diff --git a/src/addr2line.rs b/src/addr2line.rs
index f81986ee..1be023c5 100644
--- a/src/addr2line.rs
+++ b/src/addr2line.rs
@@ -107,7 +107,7 @@ impl<'a> Addr2lineModules<'a> {
                 Some(start) => addr
                     .checked_sub(start)
                     .context("address is before the beginning of the text section")?,
-                None => bail!("no code section found in module"),
+                None => return Ok(None),
             }
         };
 
diff --git a/src/bin/wasm-tools/component.rs b/src/bin/wasm-tools/component.rs
index 1b15b2f5..adc24f30 100644
--- a/src/bin/wasm-tools/component.rs
+++ b/src/bin/wasm-tools/component.rs
@@ -648,11 +648,18 @@ impl WitOpts {
                         dir.join("deps")
                     };
                     let packages_with_same_name = &names[&pkg.name.name];
+                    let packages_with_same_namespace = packages_with_same_name[&pkg.name.namespace];
                     let stem = if packages_with_same_name.len() == 1 {
-                        pkg.name.name.clone()
+                        if packages_with_same_namespace == 1 {
+                            pkg.name.name.clone()
+                        } else {
+                            pkg.name
+                                .version
+                                .as_ref()
+                                .map(|ver| format!("{}@{}", pkg.name.name, ver))
+                                .unwrap_or_else(|| pkg.name.name.clone())
+                        }
                     } else {
-                        let packages_with_same_namespace =
-                            packages_with_same_name[&pkg.name.namespace];
                         if packages_with_same_namespace == 1 {
                             format!("{}:{}", pkg.name.namespace, pkg.name.name)
                         } else {
diff --git a/src/bin/wasm-tools/validate.rs b/src/bin/wasm-tools/validate.rs
index c0268ccb..b94596b5 100644
--- a/src/bin/wasm-tools/validate.rs
+++ b/src/bin/wasm-tools/validate.rs
@@ -1,5 +1,6 @@
 use addr2line::LookupResult;
 use anyhow::{anyhow, Context, Result};
+use bitflags::Flags;
 use rayon::prelude::*;
 use std::fmt::Write;
 use std::mem;
@@ -171,37 +172,9 @@ impl Opts {
 fn parse_features(arg: &str) -> Result<WasmFeatures> {
     let mut ret = WasmFeatures::default();
 
-    const FEATURES: &[(&str, WasmFeatures)] = &[
-        ("reference-types", WasmFeatures::REFERENCE_TYPES),
-        ("function-references", WasmFeatures::FUNCTION_REFERENCES),
-        ("simd", WasmFeatures::SIMD),
-        ("threads", WasmFeatures::THREADS),
-        (
-            "shared-everything-threads",
-            WasmFeatures::SHARED_EVERYTHING_THREADS,
-        ),
-        ("bulk-memory", WasmFeatures::BULK_MEMORY),
-        ("multi-value", WasmFeatures::MULTI_VALUE),
-        ("tail-call", WasmFeatures::TAIL_CALL),
-        ("component-model", WasmFeatures::COMPONENT_MODEL),
-        (
-            "component-model-values",
-            WasmFeatures::COMPONENT_MODEL_VALUES,
-        ),
-        ("multi-memory", WasmFeatures::MULTI_MEMORY),
-        ("exception-handling", WasmFeatures::EXCEPTIONS),
-        ("memory64", WasmFeatures::MEMORY64),
-        ("extended-const", WasmFeatures::EXTENDED_CONST),
-        ("floats", WasmFeatures::FLOATS),
-        (
-            "saturating-float-to-int",
-            WasmFeatures::SATURATING_FLOAT_TO_INT,
-        ),
-        ("sign-extension", WasmFeatures::SIGN_EXTENSION),
-        ("mutable-global", WasmFeatures::MUTABLE_GLOBAL),
-        ("relaxed-simd", WasmFeatures::RELAXED_SIMD),
-        ("gc", WasmFeatures::GC),
-    ];
+    fn flag_name(flag: &bitflags::Flag<WasmFeatures>) -> String {
+        flag.name().to_lowercase().replace('_', "-")
+    }
 
     for part in arg.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) {
         let (enable, part) = if let Some(part) = part.strip_prefix("-") {
@@ -211,28 +184,27 @@ fn parse_features(arg: &str) -> Result<WasmFeatures> {
         };
         match part {
             "all" => {
-                for (name, feature) in FEATURES {
-                    // don't count this under "all" for now.
-                    if *name == "deterministic" {
-                        continue;
-                    }
-                    ret.set(*feature, enable);
+                for flag in WasmFeatures::FLAGS.iter() {
+                    ret.set(*flag.value(), enable);
                 }
             }
 
             name => {
-                let (_, feature) = FEATURES.iter().find(|(n, _)| *n == name).ok_or_else(|| {
-                    anyhow!(
-                        "unknown feature `{}`\nValid features: {}",
-                        name,
-                        FEATURES
-                            .iter()
-                            .map(|(name, _)| *name)
-                            .collect::<Vec<_>>()
-                            .join(", "),
-                    )
-                })?;
-                ret.set(*feature, enable);
+                let flag = WasmFeatures::FLAGS
+                    .iter()
+                    .find(|f| flag_name(f) == name)
+                    .ok_or_else(|| {
+                        anyhow!(
+                            "unknown feature `{}`\nValid features: {}",
+                            name,
+                            WasmFeatures::FLAGS
+                                .iter()
+                                .map(flag_name)
+                                .collect::<Vec<_>>()
+                                .join(", "),
+                        )
+                    })?;
+                ret.set(*flag.value(), enable);
             }
         }
     }


Test patch:
diff --git a/tests/cli/validate-features.wat b/tests/cli/validate-features.wat
new file mode 100644
index 00000000..3aad684f
--- /dev/null
+++ b/tests/cli/validate-features.wat
@@ -0,0 +1,5 @@
+;; FAIL: validate --features=-all %
+
+(module
+  (import "x" "y" (global v128))
+)
diff --git a/tests/cli/validate-features.wat.stderr b/tests/cli/validate-features.wat.stderr
new file mode 100644
index 00000000..45ead379
--- /dev/null
+++ b/tests/cli/validate-features.wat.stderr
@@ -0,0 +1,1 @@
+error: SIMD support is not enabled (at offset 0xb)
diff --git a/tests/cli/validate-features2.wat b/tests/cli/validate-features2.wat
new file mode 100644
index 00000000..4bf1c135
--- /dev/null
+++ b/tests/cli/validate-features2.wat
@@ -0,0 +1,6 @@
+;; RUN: validate --features=-all,simd %
+
+(module
+  (import "x" "y" (global v128))
+)
+
diff --git a/tests/cli/validate-unknown-features.wat b/tests/cli/validate-unknown-features.wat
new file mode 100644
index 00000000..daebd5c6
--- /dev/null
+++ b/tests/cli/validate-unknown-features.wat
@@ -0,0 +1,4 @@
+;; FAIL: validate --features=unknown %
+
+(module)
+
diff --git a/tests/cli/validate-unknown-features.wat.stderr b/tests/cli/validate-unknown-features.wat.stderr
new file mode 100644
index 00000000..3a72311f
--- /dev/null
+++ b/tests/cli/validate-unknown-features.wat.stderr
@@ -0,0 +1,4 @@
+error: invalid value 'unknown' for '--features <FEATURES>': unknown feature `unknown`
+Valid features: mutable-global, saturating-float-to-int, sign-extension, reference-types, multi-value, bulk-memory, simd, relaxed-simd, threads, shared-everything-threads, tail-call, floats, multi-memory, exceptions, memory64, extended-const, component-model, function-references, memory-control, gc, custom-page-sizes, component-model-values, component-model-nested-names, component-model-more-flags, legacy-exceptions
+
+For more information, try '--help'.
diff --git a/tests/cli/wit-directory-output-in-deps-folder-with-multiple-version.wit b/tests/cli/wit-directory-output-in-deps-folder-with-multiple-version.wit
new file mode 100644
index 00000000..980d99db
--- /dev/null
+++ b/tests/cli/wit-directory-output-in-deps-folder-with-multiple-version.wit
@@ -0,0 +1,16 @@
+// RUN: component wit % --out-dir %tmpdir | component wit %tmpdir
+
+package a:b@0.2.0 {
+  interface foo {}
+}
+
+package a:b {
+  interface foo {}
+}
+
+package a:c {
+  world a {
+    import a:b/foo@0.2.0;
+    import a:b/foo;
+  }
+}
\ No newline at end of file
diff --git a/tests/cli/wit-directory-output-in-deps-folder-with-multiple-version.wit.stdout b/tests/cli/wit-directory-output-in-deps-folder-with-multiple-version.wit.stdout
new file mode 100644
index 00000000..f306ee9c
--- /dev/null
+++ b/tests/cli/wit-directory-output-in-deps-folder-with-multiple-version.wit.stdout
@@ -0,0 +1,20 @@
+package a:b {
+  interface foo {
+  }
+
+}
+
+
+package a:b@0.2.0 {
+  interface foo {
+  }
+
+}
+
+
+package a:c {
+  world a {
+    import a:b/foo@0.2.0;
+    import a:b/foo;
+  }
+}
diff --git a/tests/local/legacy-exceptions.wat b/tests/local/legacy-exceptions.wat
new file mode 100644
index 00000000..f923a69a
--- /dev/null
+++ b/tests/local/legacy-exceptions.wat
@@ -0,0 +1,17 @@
+;; --enable-legacy-exceptions
+(module
+  (type (;0;) (func))
+  (func (;0;) (type 0)
+    try ;; label = @1
+      try ;; label = @2
+        try ;; label = @3
+          throw 0
+        catch_all
+          rethrow 0 (;@3;)
+        end
+      delegate 0 (;@2;)
+    catch 0
+    end
+  )
+  (tag (;0;) (type 0))
+)
diff --git a/tests/local/shared-everything-threads/array.wast b/tests/local/shared-everything-threads/array.wast
index e3fb3adb..064fcecf 100644
--- a/tests/local/shared-everything-threads/array.wast
+++ b/tests/local/shared-everything-threads/array.wast
@@ -1,4 +1,4 @@
-;; Shared array declaration syntax
+;; Shared array declaration syntax.
 (module
   (type (shared (array i8)))
   (type (sub final (shared (array i8))))
@@ -11,7 +11,7 @@
   (global (ref 2) (array.new_default 0 (i32.const 1)))
 )
 
-;; Shared arrays are distinct from non-shared arrays
+;; Shared arrays are distinct from non-shared arrays.
 (assert_invalid
   (module
     (type (shared (array i8)))
@@ -32,7 +32,7 @@
   "type mismatch"
 )
 
-;; Shared arrays may not be subtypes of non-shared arrays
+;; Shared arrays may not be subtypes of non-shared arrays.
 (assert_invalid
   (module
     (type (sub (array i8)))
@@ -41,7 +41,7 @@
   "sub type must match super type"
 )
 
-;; Non-shared arrays may not be subtypes of shared arrays
+;; Non-shared arrays may not be subtypes of shared arrays.
 (assert_invalid
   (module
     (type (sub (shared (array i8))))
@@ -50,7 +50,7 @@
   "sub type must match super type"
 )
 
-;; Shared arrays may not contain non-shared references
+;; Shared arrays may not contain non-shared references.
 (assert_invalid
   (module
     (type (shared (array anyref)))
@@ -58,12 +58,12 @@
   "must contain shared type"
 )
 
-;; But they may contain shared references
+;; But they may contain shared references.
 (module
   (type (shared (array (ref null (shared any)))))
 )
 
-;; Non-shared arrays may contain shared references
+;; Non-shared arrays may contain shared references.
 (module
   (type (array (ref null (shared any))))
 )
@@ -113,3 +113,641 @@
   (func (param (ref null $i8))
     (array.init_data $i8 0 (local.get 0) (i32.const 0) (i32.const 0) (i32.const 0)))
 )
+
+;; Check `array.atomic.rmw.*` instructions.
+(module (; get, i32, seq_cst ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-get-i32-seq_cst") (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get seq_cst $a)
+)
+
+(module (; get, i64, seq_cst ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-get-i64-seq_cst") (param $x (ref null $a)) (param $y i32) (result i64)
+    local.get $x
+    local.get $y
+    array.atomic.get seq_cst $a)
+)
+
+(module (; get, anyref, seq_cst ;)
+  (type $a (shared (array (mut (ref null (shared any))))))
+  (func (export "array-atomic-get-anyref-seq_cst") (param $x (ref null $a)) (param $y i32) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    array.atomic.get seq_cst $a)
+)
+
+(module (; get, i32, acq_rel ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-get-i32-acq_rel") (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get acq_rel $a)
+)
+
+(module (; get, i64, acq_rel ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-get-i64-acq_rel") (param $x (ref null $a)) (param $y i32) (result i64)
+    local.get $x
+    local.get $y
+    array.atomic.get acq_rel $a)
+)
+
+(module (; get, anyref, acq_rel ;)
+  (type $a (shared (array (mut (ref null (shared any))))))
+  (func (export "array-atomic-get-anyref-acq_rel") (param $x (ref null $a)) (param $y i32) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    array.atomic.get acq_rel $a)
+)
+
+(module (; get_s, i8, seq_cst ;)
+  (type $a (shared (array (mut i8))))
+  (func (export "array-atomic-get_s-i8-seq_cst") (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_s seq_cst $a)
+)
+
+(module (; get_s, i16, seq_cst ;)
+  (type $a (shared (array (mut i16))))
+  (func (export "array-atomic-get_s-i16-seq_cst") (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_s seq_cst $a)
+)
+
+(module (; get_s, i8, acq_rel ;)
+  (type $a (shared (array (mut i8))))
+  (func (export "array-atomic-get_s-i8-acq_rel") (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_s acq_rel $a)
+)
+
+(module (; get_s, i16, acq_rel ;)
+  (type $a (shared (array (mut i16))))
+  (func (export "array-atomic-get_s-i16-acq_rel") (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_s acq_rel $a)
+)
+
+(module (; get_u, i8, seq_cst ;)
+  (type $a (shared (array (mut i8))))
+  (func (export "array-atomic-get_u-i8-seq_cst") (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_u seq_cst $a)
+)
+
+(module (; get_u, i16, seq_cst ;)
+  (type $a (shared (array (mut i16))))
+  (func (export "array-atomic-get_u-i16-seq_cst") (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_u seq_cst $a)
+)
+
+(module (; get_u, i8, acq_rel ;)
+  (type $a (shared (array (mut i8))))
+  (func (export "array-atomic-get_u-i8-acq_rel") (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_u acq_rel $a)
+)
+
+(module (; get_u, i16, acq_rel ;)
+  (type $a (shared (array (mut i16))))
+  (func (export "array-atomic-get_u-i16-acq_rel") (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_u acq_rel $a)
+)
+
+(module (; set, i8, seq_cst ;)
+  (type $a (shared (array (mut i8))))
+  (func (export "array-atomic-set-i8-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set seq_cst $a)
+)
+
+(module (; set, i16, seq_cst ;)
+  (type $a (shared (array (mut i16))))
+  (func (export "array-atomic-set-i16-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set seq_cst $a)
+)
+
+(module (; set, i32, seq_cst ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-set-i32-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set seq_cst $a)
+)
+
+(module (; set, i64, seq_cst ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-set-i64-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set seq_cst $a)
+)
+
+(module (; set, anyref, seq_cst ;)
+  (type $a (shared (array (mut (ref null (shared any))))))
+  (func (export "array-atomic-set-anyref-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set seq_cst $a)
+)
+
+(module (; set, i8, acq_rel ;)
+  (type $a (shared (array (mut i8))))
+  (func (export "array-atomic-set-i8-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set acq_rel $a)
+)
+
+(module (; set, i16, acq_rel ;)
+  (type $a (shared (array (mut i16))))
+  (func (export "array-atomic-set-i16-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set acq_rel $a)
+)
+
+(module (; set, i32, acq_rel ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-set-i32-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set acq_rel $a)
+)
+
+(module (; set, i64, acq_rel ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-set-i64-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set acq_rel $a)
+)
+
+(module (; set, anyref, acq_rel ;)
+  (type $a (shared (array (mut (ref null (shared any))))))
+  (func (export "array-atomic-set-anyref-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set acq_rel $a)
+)
+
+(module (; rmw.add, i32, seq_cst ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.add-i32-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.add seq_cst $a)
+)
+
+(module (; rmw.add, i64, seq_cst ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.add-i64-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.add seq_cst $a)
+)
+
+(module (; rmw.add, i32, acq_rel ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.add-i32-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.add acq_rel $a)
+)
+
+(module (; rmw.add, i64, acq_rel ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.add-i64-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.add acq_rel $a)
+)
+
+(module (; rmw.sub, i32, seq_cst ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.sub-i32-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.sub seq_cst $a)
+)
+
+(module (; rmw.sub, i64, seq_cst ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.sub-i64-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.sub seq_cst $a)
+)
+
+(module (; rmw.sub, i32, acq_rel ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.sub-i32-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.sub acq_rel $a)
+)
+
+(module (; rmw.sub, i64, acq_rel ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.sub-i64-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.sub acq_rel $a)
+)
+
+(module (; rmw.and, i32, seq_cst ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.and-i32-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.and seq_cst $a)
+)
+
+(module (; rmw.and, i64, seq_cst ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.and-i64-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.and seq_cst $a)
+)
+
+(module (; rmw.and, i32, acq_rel ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.and-i32-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.and acq_rel $a)
+)
+
+(module (; rmw.and, i64, acq_rel ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.and-i64-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.and acq_rel $a)
+)
+
+(module (; rmw.or, i32, seq_cst ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.or-i32-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.or seq_cst $a)
+)
+
+(module (; rmw.or, i64, seq_cst ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.or-i64-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.or seq_cst $a)
+)
+
+(module (; rmw.or, i32, acq_rel ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.or-i32-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.or acq_rel $a)
+)
+
+(module (; rmw.or, i64, acq_rel ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.or-i64-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.or acq_rel $a)
+)
+
+(module (; rmw.xor, i32, seq_cst ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.xor-i32-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xor seq_cst $a)
+)
+
+(module (; rmw.xor, i64, seq_cst ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.xor-i64-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xor seq_cst $a)
+)
+
+(module (; rmw.xor, i32, acq_rel ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.xor-i32-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xor acq_rel $a)
+)
+
+(module (; rmw.xor, i64, acq_rel ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.xor-i64-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xor acq_rel $a)
+)
+
+(module (; rmw.xchg, i32, seq_cst ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.xchg-i32-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg seq_cst $a)
+)
+
+(module (; rmw.xchg, i64, seq_cst ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.xchg-i64-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg seq_cst $a)
+)
+
+(module (; rmw.xchg, anyref, seq_cst ;)
+  (type $a (shared (array (mut (ref null (shared any))))))
+  (func (export "array-atomic-rmw.xchg-anyref-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg seq_cst $a)
+)
+
+(module (; rmw.xchg, i32, acq_rel ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.xchg-i32-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg acq_rel $a)
+)
+
+(module (; rmw.xchg, i64, acq_rel ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.xchg-i64-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg acq_rel $a)
+)
+
+(module (; rmw.xchg, anyref, acq_rel ;)
+  (type $a (shared (array (mut (ref null (shared any))))))
+  (func (export "array-atomic-rmw.xchg-anyref-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg acq_rel $a)
+)
+
+(module (; rmw.cmpxchg, i32, seq_cst ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.cmpxchg-i32-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i32) (param $A i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg seq_cst $a)
+)
+
+(module (; rmw.cmpxchg, i64, seq_cst ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.cmpxchg-i64-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z i64) (param $A i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg seq_cst $a)
+)
+
+(module (; rmw.cmpxchg, eqref, seq_cst ;)
+  (type $a (shared (array (mut (ref null (shared eq))))))
+  (func (export "array-atomic-rmw.cmpxchg-eqref-seq_cst") (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared eq))) (param $A (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg seq_cst $a)
+)
+
+(module (; rmw.cmpxchg, i32, acq_rel ;)
+  (type $a (shared (array (mut i32))))
+  (func (export "array-atomic-rmw.cmpxchg-i32-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i32) (param $A i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg acq_rel $a)
+)
+
+(module (; rmw.cmpxchg, i64, acq_rel ;)
+  (type $a (shared (array (mut i64))))
+  (func (export "array-atomic-rmw.cmpxchg-i64-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z i64) (param $A i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg acq_rel $a)
+)
+
+(module (; rmw.cmpxchg, eqref, acq_rel ;)
+  (type $a (shared (array (mut (ref null (shared eq))))))
+  (func (export "array-atomic-rmw.cmpxchg-eqref-acq_rel") (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared eq))) (param $A (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg acq_rel $a)
+)
+
+(assert_invalid (; get, i8 ;)
+  (module
+    (type $a (shared (array (mut i8))))
+  (func (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get seq_cst $a)
+  )
+  "packed storage type"
+)
+(assert_invalid (; get_s, i32 ;)
+  (module
+    (type $a (shared (array (mut i32))))
+  (func (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_s seq_cst $a)
+  )
+  "non-packed storage type"
+)
+(assert_invalid (; get_s, anyref ;)
+  (module
+    (type $a (shared (array (mut (ref null (shared any))))))
+  (func (param $x (ref null $a)) (param $y i32) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    array.atomic.get_s seq_cst $a)
+  )
+  "non-packed storage type"
+)
+(assert_invalid (; get_u, i32 ;)
+  (module
+    (type $a (shared (array (mut i32))))
+  (func (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_u seq_cst $a)
+  )
+  "non-packed storage type"
+)
+(assert_invalid (; get_u, anyref ;)
+  (module
+    (type $a (shared (array (mut (ref null (shared any))))))
+  (func (param $x (ref null $a)) (param $y i32) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    array.atomic.get_u seq_cst $a)
+  )
+  "non-packed storage type"
+)
+(assert_invalid (; rmw.add, anyref ;)
+  (module
+    (type $a (shared (array (mut (ref null (shared any))))))
+  (func (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.add seq_cst $a)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.sub, anyref ;)
+  (module
+    (type $a (shared (array (mut (ref null (shared any))))))
+  (func (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.sub seq_cst $a)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.and, anyref ;)
+  (module
+    (type $a (shared (array (mut (ref null (shared any))))))
+  (func (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.and seq_cst $a)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.or, anyref ;)
+  (module
+    (type $a (shared (array (mut (ref null (shared any))))))
+  (func (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.or seq_cst $a)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.xor, anyref ;)
+  (module
+    (type $a (shared (array (mut (ref null (shared any))))))
+  (func (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xor seq_cst $a)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.xchg, i8 ;)
+  (module
+    (type $a (shared (array (mut i8))))
+  (func (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg seq_cst $a)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.cmpxchg, i8 ;)
+  (module
+    (type $a (shared (array (mut i8))))
+  (func (param $x (ref null $a)) (param $y i32) (param $z i32) (param $A i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg seq_cst $a)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.cmpxchg, anyref ;)
+  (module
+    (type $a (shared (array (mut (ref null (shared any))))))
+  (func (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any))) (param $A (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg seq_cst $a)
+  )
+  "invalid type"
+)
diff --git a/tests/local/shared-everything-threads/struct.wast b/tests/local/shared-everything-threads/struct.wast
index 91b1cec2..cd666463 100644
--- a/tests/local/shared-everything-threads/struct.wast
+++ b/tests/local/shared-everything-threads/struct.wast
@@ -1,4 +1,4 @@
-;; Shared struct declaration syntax
+;; Shared struct declaration syntax.
 (module
   (type (shared (struct)))
   (type (sub final (shared (struct))))
@@ -11,7 +11,7 @@
   (global (ref 2) (struct.new 0))
 )
 
-;; Shared structs are distinct from non-shared structs
+;; Shared structs are distinct from non-shared structs.
 (assert_invalid
   (module
     (type (shared (struct)))
@@ -32,7 +32,7 @@
   "type mismatch"
 )
 
-;; Shared structs may not be subtypes of non-shared structs
+;; Shared structs may not be subtypes of non-shared structs.
 (assert_invalid
   (module
     (type (sub (struct)))
@@ -41,7 +41,7 @@
   "must match super type"
 )
 
-;; Non-shared structs may not be subtypes of shared structs
+;; Non-shared structs may not be subtypes of shared structs.
 (assert_invalid
   (module
     (type (sub (shared (struct))))
@@ -50,7 +50,7 @@
   "must match super type"
 )
 
-;; Shared structs may not contain non-shared references
+;; Shared structs may not contain non-shared references.
 (assert_invalid
   (module
     (type (shared (struct (field anyref))))
@@ -58,12 +58,12 @@
   "must contain shared type"
 )
 
-;; But they may contain shared references
+;; But they may contain shared references.
 (module
   (type (shared (struct (field (ref null (shared any))))))
 )
 
-;; Non-shared structs may contain shared references
+;; Non-shared structs may contain shared references.
 (module
   (type (struct (field (ref null (shared any)))))
 )
@@ -90,3 +90,358 @@
   (func (param (ref null $i8))
     (struct.set $i8 0 (local.get 0) (i32.const 0)))
 )
+
+;; Check struct.atomic.rmw.* instructions
+(module
+  (type $s (shared (struct
+    (field $i8 (mut i8))
+    (field $i16 (mut i16))
+    (field $i32 (mut i32))
+    (field $i64 (mut i64))
+    (field $anyref (mut (ref null (shared any))))
+    (field $eqref (mut (ref null (shared eq)))))))
+  (func (export "struct-atomic-get-i32-seq_cst") (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get seq_cst $s $i32)
+  (func (export "struct-atomic-get-i64-seq_cst") (param $x (ref null $s)) (result i64)
+    local.get $x
+    struct.atomic.get seq_cst $s $i64)
+  (func (export "struct-atomic-get-anyref-seq_cst") (param $x (ref null $s)) (result (ref null (shared any)))
+    local.get $x
+    struct.atomic.get seq_cst $s $anyref)
+  (func (export "struct-atomic-get-i32-acq_rel") (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get acq_rel $s $i32)
+  (func (export "struct-atomic-get-i64-acq_rel") (param $x (ref null $s)) (result i64)
+    local.get $x
+    struct.atomic.get acq_rel $s $i64)
+  (func (export "struct-atomic-get-anyref-acq_rel") (param $x (ref null $s)) (result (ref null (shared any)))
+    local.get $x
+    struct.atomic.get acq_rel $s $anyref)
+  (func (export "struct-atomic-get_s-i8-seq_cst") (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_s seq_cst $s $i8)
+  (func (export "struct-atomic-get_s-i16-seq_cst") (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_s seq_cst $s $i16)
+  (func (export "struct-atomic-get_s-i8-acq_rel") (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_s acq_rel $s $i8)
+  (func (export "struct-atomic-get_s-i16-acq_rel") (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_s acq_rel $s $i16)
+  (func (export "struct-atomic-get_u-i8-seq_cst") (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_u seq_cst $s $i8)
+  (func (export "struct-atomic-get_u-i16-seq_cst") (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_u seq_cst $s $i16)
+  (func (export "struct-atomic-get_u-i8-acq_rel") (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_u acq_rel $s $i8)
+  (func (export "struct-atomic-get_u-i16-acq_rel") (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_u acq_rel $s $i16)
+  (func (export "struct-atomic-set-i8-seq_cst") (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set seq_cst $s $i8)
+  (func (export "struct-atomic-set-i16-seq_cst") (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set seq_cst $s $i16)
+  (func (export "struct-atomic-set-i32-seq_cst") (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set seq_cst $s $i32)
+  (func (export "struct-atomic-set-i64-seq_cst") (param $x (ref null $s)) (param $y i64)
+    local.get $x
+    local.get $y
+    struct.atomic.set seq_cst $s $i64)
+  (func (export "struct-atomic-set-anyref-seq_cst") (param $x (ref null $s)) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.set seq_cst $s $anyref)
+  (func (export "struct-atomic-set-i8-acq_rel") (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set acq_rel $s $i8)
+  (func (export "struct-atomic-set-i16-acq_rel") (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set acq_rel $s $i16)
+  (func (export "struct-atomic-set-i32-acq_rel") (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set acq_rel $s $i32)
+  (func (export "struct-atomic-set-i64-acq_rel") (param $x (ref null $s)) (param $y i64)
+    local.get $x
+    local.get $y
+    struct.atomic.set acq_rel $s $i64)
+  (func (export "struct-atomic-set-anyref-acq_rel") (param $x (ref null $s)) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.set acq_rel $s $anyref)
+  (func (export "struct-atomic-rmw.add-i32-seq_cst") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.add seq_cst $s $i32)
+  (func (export "struct-atomic-rmw.add-i64-seq_cst") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.add seq_cst $s $i64)
+  (func (export "struct-atomic-rmw.add-i32-acq_rel") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.add acq_rel $s $i32)
+  (func (export "struct-atomic-rmw.add-i64-acq_rel") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.add acq_rel $s $i64)
+  (func (export "struct-atomic-rmw.sub-i32-seq_cst") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.sub seq_cst $s $i32)
+  (func (export "struct-atomic-rmw.sub-i64-seq_cst") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.sub seq_cst $s $i64)
+  (func (export "struct-atomic-rmw.sub-i32-acq_rel") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.sub acq_rel $s $i32)
+  (func (export "struct-atomic-rmw.sub-i64-acq_rel") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.sub acq_rel $s $i64)
+  (func (export "struct-atomic-rmw.and-i32-seq_cst") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.and seq_cst $s $i32)
+  (func (export "struct-atomic-rmw.and-i64-seq_cst") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.and seq_cst $s $i64)
+  (func (export "struct-atomic-rmw.and-i32-acq_rel") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.and acq_rel $s $i32)
+  (func (export "struct-atomic-rmw.and-i64-acq_rel") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.and acq_rel $s $i64)
+  (func (export "struct-atomic-rmw.or-i32-seq_cst") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.or seq_cst $s $i32)
+  (func (export "struct-atomic-rmw.or-i64-seq_cst") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.or seq_cst $s $i64)
+  (func (export "struct-atomic-rmw.or-i32-acq_rel") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.or acq_rel $s $i32)
+  (func (export "struct-atomic-rmw.or-i64-acq_rel") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.or acq_rel $s $i64)
+  (func (export "struct-atomic-rmw.xor-i32-seq_cst") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xor seq_cst $s $i32)
+  (func (export "struct-atomic-rmw.xor-i64-seq_cst") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xor seq_cst $s $i64)
+  (func (export "struct-atomic-rmw.xor-i32-acq_rel") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xor acq_rel $s $i32)
+  (func (export "struct-atomic-rmw.xor-i64-acq_rel") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xor acq_rel $s $i64)
+  (func (export "struct-atomic-rmw.xchg-i32-seq_cst") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg seq_cst $s $i32)
+  (func (export "struct-atomic-rmw.xchg-i64-seq_cst") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg seq_cst $s $i64)
+  (func (export "struct-atomic-rmw.xchg-anyref-seq_cst") (param $x (ref null $s)) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg seq_cst $s $anyref)
+  (func (export "struct-atomic-rmw.xchg-i32-acq_rel") (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg acq_rel $s $i32)
+  (func (export "struct-atomic-rmw.xchg-i64-acq_rel") (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg acq_rel $s $i64)
+  (func (export "struct-atomic-rmw.xchg-anyref-acq_rel") (param $x (ref null $s)) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg acq_rel $s $anyref)
+  (func (export "struct-atomic-rmw.cmpxchg-i32-seq_cst") (param $x (ref null $s)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg seq_cst $s $i32)
+  (func (export "struct-atomic-rmw.cmpxchg-i64-seq_cst") (param $x (ref null $s)) (param $y i64) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg seq_cst $s $i64)
+  (func (export "struct-atomic-rmw.cmpxchg-eqref-seq_cst") (param $x (ref null $s)) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg seq_cst $s $eqref)
+  (func (export "struct-atomic-rmw.cmpxchg-i32-acq_rel") (param $x (ref null $s)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg acq_rel $s $i32)
+  (func (export "struct-atomic-rmw.cmpxchg-i64-acq_rel") (param $x (ref null $s)) (param $y i64) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg acq_rel $s $i64)
+  (func (export "struct-atomic-rmw.cmpxchg-eqref-acq_rel") (param $x (ref null $s)) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg acq_rel $s $eqref)
+)
+
+(assert_invalid (; get, i8 ;)
+  (module
+    (type $s (shared (struct (field $i8 (mut i8)))))
+  (func (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get seq_cst $s $i8)
+  )
+  "non-packed storage type"
+)
+(assert_invalid (; get_s, i32 ;)
+  (module
+    (type $s (shared (struct (field $i32 (mut i32)))))
+  (func (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_s seq_cst $s $i32)
+  )
+  "non-packed storage types"
+)
+(assert_invalid (; get_s, anyref ;)
+  (module
+    (type $s (shared (struct (field $anyref (mut (ref null (shared any)))))))
+  (func (param $x (ref null $s)) (result (ref null (shared any)))
+    local.get $x
+    struct.atomic.get_s seq_cst $s $anyref)
+  )
+  "non-packed storage types"
+)
+(assert_invalid (; get_u, i32 ;)
+  (module
+    (type $s (shared (struct (field $i32 (mut i32)))))
+  (func (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_u seq_cst $s $i32)
+  )
+  "non-packed storage types"
+)
+(assert_invalid (; get_u, anyref ;)
+  (module
+    (type $s (shared (struct (field $anyref (mut (ref null (shared any)))))))
+  (func (param $x (ref null $s)) (result (ref null (shared any)))
+    local.get $x
+    struct.atomic.get_u seq_cst $s $anyref)
+  )
+  "non-packed storage types"
+)
+(assert_invalid (; rmw.add, anyref ;)
+  (module
+    (type $s (shared (struct (field $anyref (mut (ref null (shared any)))))))
+  (func (param $x (ref null $s)) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.add seq_cst $s $anyref)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.sub, anyref ;)
+  (module
+    (type $s (shared (struct (field $anyref (mut (ref null (shared any)))))))
+  (func (param $x (ref null $s)) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.sub seq_cst $s $anyref)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.and, anyref ;)
+  (module
+    (type $s (shared (struct (field $anyref (mut (ref null (shared any)))))))
+  (func (param $x (ref null $s)) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.and seq_cst $s $anyref)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.or, anyref ;)
+  (module
+    (type $s (shared (struct (field $anyref (mut (ref null (shared any)))))))
+  (func (param $x (ref null $s)) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.or seq_cst $s $anyref)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.xor, anyref ;)
+  (module
+    (type $s (shared (struct (field $anyref (mut (ref null (shared any)))))))
+  (func (param $x (ref null $s)) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xor seq_cst $s $anyref)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.xchg, i8 ;)
+  (module
+    (type $s (shared (struct (field $i8 (mut i8)))))
+  (func (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg seq_cst $s $i8)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.cmpxchg, i8 ;)
+  (module
+    (type $s (shared (struct (field $i8 (mut i8)))))
+  (func (param $x (ref null $s)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg seq_cst $s $i8)
+  )
+  "invalid type"
+)
+(assert_invalid (; rmw.cmpxchg, anyref ;)
+  (module
+    (type $s (shared (struct (field $anyref (mut (ref null (shared any)))))))
+  (func (param $x (ref null $s)) (param $y (ref null (shared any))) (param $z (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg seq_cst $s $anyref)
+  )
+  "invalid type"
+)
diff --git a/tests/local/shared-everything-threads/table.wast b/tests/local/shared-everything-threads/table.wast
index 0b4eedc2..b733ec4c 100644
--- a/tests/local/shared-everything-threads/table.wast
+++ b/tests/local/shared-everything-threads/table.wast
@@ -41,3 +41,134 @@
     (type $t (func))
     (table shared 0 (ref $t)))
   "shared tables must have a shared element type")
+
+;; Check `table.atomic.*` instructions.
+(module (;eq;)
+  (table $a (import "spectest" "table_eq") shared 1 (ref null (shared eq)))
+  (table $b shared 1 (ref null (shared eq)))
+  (func (export "table-atomic-get-eq-seq_cst-$a") (param $x i32) (result (ref null (shared eq)))
+    local.get $x
+    table.atomic.get seq_cst $a)
+  (func (export "table-atomic-get-eq-seq_cst-$b") (param $x i32) (result (ref null (shared eq)))
+    local.get $x
+    table.atomic.get seq_cst $b)
+  (func (export "table-atomic-get-eq-acq_rel-$a") (param $x i32) (result (ref null (shared eq)))
+    local.get $x
+    table.atomic.get acq_rel $a)
+  (func (export "table-atomic-get-eq-acq_rel-$b") (param $x i32) (result (ref null (shared eq)))
+    local.get $x
+    table.atomic.get acq_rel $b)
+  (func (export "table-atomic-set-eq-seq_cst-$a") (param $x i32) (param $y (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.set seq_cst $a)
+  (func (export "table-atomic-set-eq-seq_cst-$b") (param $x i32) (param $y (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.set seq_cst $b)
+  (func (export "table-atomic-set-eq-acq_rel-$a") (param $x i32) (param $y (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.set acq_rel $a)
+  (func (export "table-atomic-set-eq-acq_rel-$b") (param $x i32) (param $y (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.set acq_rel $b)
+  (func (export "table-atomic-rmw.xchg-eq-seq_cst-$a") (param $x i32) (param $y (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg seq_cst $a)
+  (func (export "table-atomic-rmw.xchg-eq-seq_cst-$b") (param $x i32) (param $y (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg seq_cst $b)
+  (func (export "table-atomic-rmw.xchg-eq-acq_rel-$a") (param $x i32) (param $y (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg acq_rel $a)
+  (func (export "table-atomic-rmw.xchg-eq-acq_rel-$b") (param $x i32) (param $y (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg acq_rel $b)
+  (func (export "table-atomic-rmw.cmpxchg-eq-seq_cst-$a") (param $x i32) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    table.atomic.rmw.cmpxchg seq_cst $a)
+  (func (export "table-atomic-rmw.cmpxchg-eq-seq_cst-$b") (param $x i32) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    table.atomic.rmw.cmpxchg seq_cst $b)
+  (func (export "table-atomic-rmw.cmpxchg-eq-acq_rel-$a") (param $x i32) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    table.atomic.rmw.cmpxchg acq_rel $a)
+  (func (export "table-atomic-rmw.cmpxchg-eq-acq_rel-$b") (param $x i32) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    table.atomic.rmw.cmpxchg acq_rel $b)
+)
+
+(module (;any;)
+  (table $a (import "spectest" "table_any") shared 1 (ref null (shared any)))
+  (table $b shared 1 (ref null (shared any)))
+  (func (export "table-atomic-get-any-seq_cst-$a") (param $x i32) (result (ref null (shared any)))
+    local.get $x
+    table.atomic.get seq_cst $a)
+  (func (export "table-atomic-get-any-seq_cst-$b") (param $x i32) (result (ref null (shared any)))
+    local.get $x
+    table.atomic.get seq_cst $b)
+  (func (export "table-atomic-get-any-acq_rel-$a") (param $x i32) (result (ref null (shared any)))
+    local.get $x
+    table.atomic.get acq_rel $a)
+  (func (export "table-atomic-get-any-acq_rel-$b") (param $x i32) (result (ref null (shared any)))
+    local.get $x
+    table.atomic.get acq_rel $b)
+  (func (export "table-atomic-set-any-seq_cst-$a") (param $x i32) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.set seq_cst $a)
+  (func (export "table-atomic-set-any-seq_cst-$b") (param $x i32) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.set seq_cst $b)
+  (func (export "table-atomic-set-any-acq_rel-$a") (param $x i32) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.set acq_rel $a)
+  (func (export "table-atomic-set-any-acq_rel-$b") (param $x i32) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.set acq_rel $b)
+  (func (export "table-atomic-rmw.xchg-any-seq_cst-$a") (param $x i32) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg seq_cst $a)
+  (func (export "table-atomic-rmw.xchg-any-seq_cst-$b") (param $x i32) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg seq_cst $b)
+  (func (export "table-atomic-rmw.xchg-any-acq_rel-$a") (param $x i32) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg acq_rel $a)
+  (func (export "table-atomic-rmw.xchg-any-acq_rel-$b") (param $x i32) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg acq_rel $b)
+  ;; table.atomic.rmw.cmpxchg only works with subtypes of eqref.
+)
+
+;; Check that cmpxchg only works with eqref subtypes.
+(assert_invalid
+  (module
+    (table $a shared 0 (ref null (shared any)))
+    (func (param $x i32) (param $y (ref null (shared any))) (param $z (ref null (shared any))) (result (ref null (shared any)))
+      local.get $x
+      local.get $y
+      local.get $z
+      table.atomic.rmw.cmpxchg seq_cst $a))
+  "invalid type")
diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs
index 6f1b64c7..4a8e4f2b 100644
--- a/tests/roundtrip.rs
+++ b/tests/roundtrip.rs
@@ -579,7 +579,8 @@ impl TestState {
             & !WasmFeatures::SHARED_EVERYTHING_THREADS
             & !WasmFeatures::COMPONENT_MODEL
             & !WasmFeatures::COMPONENT_MODEL_NESTED_NAMES
-            & !WasmFeatures::COMPONENT_MODEL_MORE_FLAGS;
+            & !WasmFeatures::COMPONENT_MODEL_MORE_FLAGS
+            & !WasmFeatures::LEGACY_EXCEPTIONS;
         for part in test.iter().filter_map(|t| t.to_str()) {
             match part {
                 "testsuite" => {
@@ -604,6 +605,7 @@ impl TestState {
                 }
                 "simd" => features.insert(WasmFeatures::SIMD),
                 "exception-handling" => features.insert(WasmFeatures::EXCEPTIONS),
+                "legacy-exceptions.wat" => features.insert(WasmFeatures::LEGACY_EXCEPTIONS),
                 "tail-call" => features.insert(WasmFeatures::TAIL_CALL),
                 "memory64" => features.insert(WasmFeatures::MEMORY64),
                 "component-model" => features.insert(WasmFeatures::COMPONENT_MODEL),
@@ -655,11 +657,13 @@ fn error_matches(error: &str, message: &str) -> bool {
         || message == "alignment must be a power of two"
         || message == "i32 constant out of range"
         || message == "constant expression required"
+        || message == "legacy exceptions support is not enabled"
     {
         return error.contains("expected ")
             || error.contains("constant out of range")
             || error.contains("extra tokens remaining")
-            || error.contains("unimplemented validation of deprecated opcode");
+            || error.contains("unimplemented validation of deprecated opcode")
+            || error.contains("legacy exceptions support is not enabled");
     }
 
     if message == "illegal character" {
diff --git a/tests/snapshots/local/legacy-exceptions.wat.print b/tests/snapshots/local/legacy-exceptions.wat.print
new file mode 100644
index 00000000..8ae9aa1a
--- /dev/null
+++ b/tests/snapshots/local/legacy-exceptions.wat.print
@@ -0,0 +1,16 @@
+(module
+  (type (;0;) (func))
+  (func (;0;) (type 0)
+    try ;; label = @1
+      try ;; label = @2
+        try ;; label = @3
+          throw 0
+        catch_all
+          rethrow 0 (;@3;)
+        end
+      delegate 0 (;@1;)
+    catch 0
+    end
+  )
+  (tag (;0;) (type 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast.json b/tests/snapshots/local/shared-everything-threads/array.wast.json
index 03bf81d7..51189866 100644
--- a/tests/snapshots/local/shared-everything-threads/array.wast.json
+++ b/tests/snapshots/local/shared-everything-threads/array.wast.json
@@ -55,6 +55,377 @@
       "type": "module",
       "line": 72,
       "filename": "array.8.wasm"
+    },
+    {
+      "type": "module",
+      "line": 118,
+      "filename": "array.9.wasm"
+    },
+    {
+      "type": "module",
+      "line": 126,
+      "filename": "array.10.wasm"
+    },
+    {
+      "type": "module",
+      "line": 134,
+      "filename": "array.11.wasm"
+    },
+    {
+      "type": "module",
+      "line": 142,
+      "filename": "array.12.wasm"
+    },
+    {
+      "type": "module",
+      "line": 150,
+      "filename": "array.13.wasm"
+    },
+    {
+      "type": "module",
+      "line": 158,
+      "filename": "array.14.wasm"
+    },
+    {
+      "type": "module",
+      "line": 166,
+      "filename": "array.15.wasm"
+    },
+    {
+      "type": "module",
+      "line": 174,
+      "filename": "array.16.wasm"
+    },
+    {
+      "type": "module",
+      "line": 182,
+      "filename": "array.17.wasm"
+    },
+    {
+      "type": "module",
+      "line": 190,
+      "filename": "array.18.wasm"
+    },
+    {
+      "type": "module",
+      "line": 198,
+      "filename": "array.19.wasm"
+    },
+    {
+      "type": "module",
+      "line": 206,
+      "filename": "array.20.wasm"
+    },
+    {
+      "type": "module",
+      "line": 214,
+      "filename": "array.21.wasm"
+    },
+    {
+      "type": "module",
+      "line": 222,
+      "filename": "array.22.wasm"
+    },
+    {
+      "type": "module",
+      "line": 230,
+      "filename": "array.23.wasm"
+    },
+    {
+      "type": "module",
+      "line": 239,
+      "filename": "array.24.wasm"
+    },
+    {
+      "type": "module",
+      "line": 248,
+      "filename": "array.25.wasm"
+    },
+    {
+      "type": "module",
+      "line": 257,
+      "filename": "array.26.wasm"
+    },
+    {
+      "type": "module",
+      "line": 266,
+      "filename": "array.27.wasm"
+    },
+    {
+      "type": "module",
+      "line": 275,
+      "filename": "array.28.wasm"
+    },
+    {
+      "type": "module",
+      "line": 284,
+      "filename": "array.29.wasm"
+    },
+    {
+      "type": "module",
+      "line": 293,
+      "filename": "array.30.wasm"
+    },
+    {
+      "type": "module",
+      "line": 302,
+      "filename": "array.31.wasm"
+    },
+    {
+      "type": "module",
+      "line": 311,
+      "filename": "array.32.wasm"
+    },
+    {
+      "type": "module",
+      "line": 320,
+      "filename": "array.33.wasm"
+    },
+    {
+      "type": "module",
+      "line": 329,
+      "filename": "array.34.wasm"
+    },
+    {
+      "type": "module",
+      "line": 338,
+      "filename": "array.35.wasm"
+    },
+    {
+      "type": "module",
+      "line": 347,
+      "filename": "array.36.wasm"
+    },
+    {
+      "type": "module",
+      "line": 356,
+      "filename": "array.37.wasm"
+    },
+    {
+      "type": "module",
+      "line": 365,
+      "filename": "array.38.wasm"
+    },
+    {
+      "type": "module",
+      "line": 374,
+      "filename": "array.39.wasm"
+    },
+    {
+      "type": "module",
+      "line": 383,
+      "filename": "array.40.wasm"
+    },
+    {
+      "type": "module",
+      "line": 392,
+      "filename": "array.41.wasm"
+    },
+    {
+      "type": "module",
+      "line": 401,
+      "filename": "array.42.wasm"
+    },
+    {
+      "type": "module",
+      "line": 410,
+      "filename": "array.43.wasm"
+    },
+    {
+      "type": "module",
+      "line": 419,
+      "filename": "array.44.wasm"
+    },
+    {
+      "type": "module",
+      "line": 428,
+      "filename": "array.45.wasm"
+    },
+    {
+      "type": "module",
+      "line": 437,
+      "filename": "array.46.wasm"
+    },
+    {
+      "type": "module",
+      "line": 446,
+      "filename": "array.47.wasm"
+    },
+    {
+      "type": "module",
+      "line": 455,
+      "filename": "array.48.wasm"
+    },
+    {
+      "type": "module",
+      "line": 464,
+      "filename": "array.49.wasm"
+    },
+    {
+      "type": "module",
+      "line": 473,
+      "filename": "array.50.wasm"
+    },
+    {
+      "type": "module",
+      "line": 482,
+      "filename": "array.51.wasm"
+    },
+    {
+      "type": "module",
+      "line": 491,
+      "filename": "array.52.wasm"
+    },
+    {
+      "type": "module",
+      "line": 500,
+      "filename": "array.53.wasm"
+    },
+    {
+      "type": "module",
+      "line": 509,
+      "filename": "array.54.wasm"
+    },
+    {
+      "type": "module",
+      "line": 518,
+      "filename": "array.55.wasm"
+    },
+    {
+      "type": "module",
+      "line": 527,
+      "filename": "array.56.wasm"
+    },
+    {
+      "type": "module",
+      "line": 536,
+      "filename": "array.57.wasm"
+    },
+    {
+      "type": "module",
+      "line": 545,
+      "filename": "array.58.wasm"
+    },
+    {
+      "type": "module",
+      "line": 554,
+      "filename": "array.59.wasm"
+    },
+    {
+      "type": "module",
+      "line": 564,
+      "filename": "array.60.wasm"
+    },
+    {
+      "type": "module",
+      "line": 574,
+      "filename": "array.61.wasm"
+    },
+    {
+      "type": "module",
+      "line": 584,
+      "filename": "array.62.wasm"
+    },
+    {
+      "type": "module",
+      "line": 594,
+      "filename": "array.63.wasm"
+    },
+    {
+      "type": "module",
+      "line": 604,
+      "filename": "array.64.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 615,
+      "filename": "array.65.wasm",
+      "text": "packed storage type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 625,
+      "filename": "array.66.wasm",
+      "text": "non-packed storage type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 635,
+      "filename": "array.67.wasm",
+      "text": "non-packed storage type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 645,
+      "filename": "array.68.wasm",
+      "text": "non-packed storage type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 655,
+      "filename": "array.69.wasm",
+      "text": "non-packed storage type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 665,
+      "filename": "array.70.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 676,
+      "filename": "array.71.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 687,
+      "filename": "array.72.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 698,
+      "filename": "array.73.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 709,
+      "filename": "array.74.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 720,
+      "filename": "array.75.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 731,
+      "filename": "array.76.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 743,
+      "filename": "array.77.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
     }
   ]
 }
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/10.print b/tests/snapshots/local/shared-everything-threads/array.wast/10.print
new file mode 100644
index 00000000..f733e005
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/10.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i64)
+    local.get $x
+    local.get $y
+    array.atomic.get seq_cst $a
+  )
+  (export "array-atomic-get-i64-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/11.print b/tests/snapshots/local/shared-everything-threads/array.wast/11.print
new file mode 100644
index 00000000..76dbd3ec
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/11.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut (ref null (shared any))))))
+  (type (;1;) (func (param (ref null $a) i32) (result (ref null (shared any)))))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    array.atomic.get seq_cst $a
+  )
+  (export "array-atomic-get-anyref-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/12.print b/tests/snapshots/local/shared-everything-threads/array.wast/12.print
new file mode 100644
index 00000000..5cf56083
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/12.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get acq_rel $a
+  )
+  (export "array-atomic-get-i32-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/13.print b/tests/snapshots/local/shared-everything-threads/array.wast/13.print
new file mode 100644
index 00000000..b4b3919e
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/13.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i64)
+    local.get $x
+    local.get $y
+    array.atomic.get acq_rel $a
+  )
+  (export "array-atomic-get-i64-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/14.print b/tests/snapshots/local/shared-everything-threads/array.wast/14.print
new file mode 100644
index 00000000..189c2ea0
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/14.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut (ref null (shared any))))))
+  (type (;1;) (func (param (ref null $a) i32) (result (ref null (shared any)))))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    array.atomic.get acq_rel $a
+  )
+  (export "array-atomic-get-anyref-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/15.print b/tests/snapshots/local/shared-everything-threads/array.wast/15.print
new file mode 100644
index 00000000..00e0b300
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/15.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i8))))
+  (type (;1;) (func (param (ref null $a) i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_s seq_cst $a
+  )
+  (export "array-atomic-get_s-i8-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/16.print b/tests/snapshots/local/shared-everything-threads/array.wast/16.print
new file mode 100644
index 00000000..82d7db16
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/16.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i16))))
+  (type (;1;) (func (param (ref null $a) i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_s seq_cst $a
+  )
+  (export "array-atomic-get_s-i16-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/17.print b/tests/snapshots/local/shared-everything-threads/array.wast/17.print
new file mode 100644
index 00000000..0794e778
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/17.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i8))))
+  (type (;1;) (func (param (ref null $a) i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_s acq_rel $a
+  )
+  (export "array-atomic-get_s-i8-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/18.print b/tests/snapshots/local/shared-everything-threads/array.wast/18.print
new file mode 100644
index 00000000..f7a27cdc
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/18.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i16))))
+  (type (;1;) (func (param (ref null $a) i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_s acq_rel $a
+  )
+  (export "array-atomic-get_s-i16-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/19.print b/tests/snapshots/local/shared-everything-threads/array.wast/19.print
new file mode 100644
index 00000000..2ccc5994
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/19.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i8))))
+  (type (;1;) (func (param (ref null $a) i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_u seq_cst $a
+  )
+  (export "array-atomic-get_u-i8-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/20.print b/tests/snapshots/local/shared-everything-threads/array.wast/20.print
new file mode 100644
index 00000000..144068c5
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/20.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i16))))
+  (type (;1;) (func (param (ref null $a) i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_u seq_cst $a
+  )
+  (export "array-atomic-get_u-i16-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/21.print b/tests/snapshots/local/shared-everything-threads/array.wast/21.print
new file mode 100644
index 00000000..e5bed640
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/21.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i8))))
+  (type (;1;) (func (param (ref null $a) i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_u acq_rel $a
+  )
+  (export "array-atomic-get_u-i8-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/22.print b/tests/snapshots/local/shared-everything-threads/array.wast/22.print
new file mode 100644
index 00000000..069d3eb6
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/22.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i16))))
+  (type (;1;) (func (param (ref null $a) i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get_u acq_rel $a
+  )
+  (export "array-atomic-get_u-i16-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/23.print b/tests/snapshots/local/shared-everything-threads/array.wast/23.print
new file mode 100644
index 00000000..b1727c32
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/23.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i8))))
+  (type (;1;) (func (param (ref null $a) i32 i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set seq_cst $a
+  )
+  (export "array-atomic-set-i8-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/24.print b/tests/snapshots/local/shared-everything-threads/array.wast/24.print
new file mode 100644
index 00000000..95641f94
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/24.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i16))))
+  (type (;1;) (func (param (ref null $a) i32 i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set seq_cst $a
+  )
+  (export "array-atomic-set-i16-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/25.print b/tests/snapshots/local/shared-everything-threads/array.wast/25.print
new file mode 100644
index 00000000..b8e21b1a
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/25.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set seq_cst $a
+  )
+  (export "array-atomic-set-i32-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/26.print b/tests/snapshots/local/shared-everything-threads/array.wast/26.print
new file mode 100644
index 00000000..6e11fb3a
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/26.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set seq_cst $a
+  )
+  (export "array-atomic-set-i64-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/27.print b/tests/snapshots/local/shared-everything-threads/array.wast/27.print
new file mode 100644
index 00000000..db65bed1
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/27.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut (ref null (shared any))))))
+  (type (;1;) (func (param (ref null $a) i32 (ref null (shared any)))))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set seq_cst $a
+  )
+  (export "array-atomic-set-anyref-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/28.print b/tests/snapshots/local/shared-everything-threads/array.wast/28.print
new file mode 100644
index 00000000..941e3f0d
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/28.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i8))))
+  (type (;1;) (func (param (ref null $a) i32 i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set acq_rel $a
+  )
+  (export "array-atomic-set-i8-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/29.print b/tests/snapshots/local/shared-everything-threads/array.wast/29.print
new file mode 100644
index 00000000..4e3d7c91
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/29.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i16))))
+  (type (;1;) (func (param (ref null $a) i32 i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set acq_rel $a
+  )
+  (export "array-atomic-set-i16-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/30.print b/tests/snapshots/local/shared-everything-threads/array.wast/30.print
new file mode 100644
index 00000000..17aec3ab
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/30.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set acq_rel $a
+  )
+  (export "array-atomic-set-i32-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/31.print b/tests/snapshots/local/shared-everything-threads/array.wast/31.print
new file mode 100644
index 00000000..43139589
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/31.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set acq_rel $a
+  )
+  (export "array-atomic-set-i64-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/32.print b/tests/snapshots/local/shared-everything-threads/array.wast/32.print
new file mode 100644
index 00000000..ed1acbaf
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/32.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut (ref null (shared any))))))
+  (type (;1;) (func (param (ref null $a) i32 (ref null (shared any)))))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.set acq_rel $a
+  )
+  (export "array-atomic-set-anyref-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/33.print b/tests/snapshots/local/shared-everything-threads/array.wast/33.print
new file mode 100644
index 00000000..d230670c
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/33.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.add seq_cst $a
+  )
+  (export "array-atomic-rmw.add-i32-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/34.print b/tests/snapshots/local/shared-everything-threads/array.wast/34.print
new file mode 100644
index 00000000..28ea1094
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/34.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.add seq_cst $a
+  )
+  (export "array-atomic-rmw.add-i64-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/35.print b/tests/snapshots/local/shared-everything-threads/array.wast/35.print
new file mode 100644
index 00000000..8cc27f27
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/35.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.add acq_rel $a
+  )
+  (export "array-atomic-rmw.add-i32-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/36.print b/tests/snapshots/local/shared-everything-threads/array.wast/36.print
new file mode 100644
index 00000000..3be7d18d
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/36.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.add acq_rel $a
+  )
+  (export "array-atomic-rmw.add-i64-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/37.print b/tests/snapshots/local/shared-everything-threads/array.wast/37.print
new file mode 100644
index 00000000..36af9d86
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/37.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.sub seq_cst $a
+  )
+  (export "array-atomic-rmw.sub-i32-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/38.print b/tests/snapshots/local/shared-everything-threads/array.wast/38.print
new file mode 100644
index 00000000..5bb94f29
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/38.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.sub seq_cst $a
+  )
+  (export "array-atomic-rmw.sub-i64-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/39.print b/tests/snapshots/local/shared-everything-threads/array.wast/39.print
new file mode 100644
index 00000000..0b4badbb
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/39.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.sub acq_rel $a
+  )
+  (export "array-atomic-rmw.sub-i32-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/40.print b/tests/snapshots/local/shared-everything-threads/array.wast/40.print
new file mode 100644
index 00000000..ba017726
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/40.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.sub acq_rel $a
+  )
+  (export "array-atomic-rmw.sub-i64-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/41.print b/tests/snapshots/local/shared-everything-threads/array.wast/41.print
new file mode 100644
index 00000000..5c610307
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/41.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.and seq_cst $a
+  )
+  (export "array-atomic-rmw.and-i32-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/42.print b/tests/snapshots/local/shared-everything-threads/array.wast/42.print
new file mode 100644
index 00000000..2edfd317
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/42.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.and seq_cst $a
+  )
+  (export "array-atomic-rmw.and-i64-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/43.print b/tests/snapshots/local/shared-everything-threads/array.wast/43.print
new file mode 100644
index 00000000..9a6e89fc
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/43.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.and acq_rel $a
+  )
+  (export "array-atomic-rmw.and-i32-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/44.print b/tests/snapshots/local/shared-everything-threads/array.wast/44.print
new file mode 100644
index 00000000..f82346cd
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/44.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.and acq_rel $a
+  )
+  (export "array-atomic-rmw.and-i64-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/45.print b/tests/snapshots/local/shared-everything-threads/array.wast/45.print
new file mode 100644
index 00000000..59adfe73
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/45.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.or seq_cst $a
+  )
+  (export "array-atomic-rmw.or-i32-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/46.print b/tests/snapshots/local/shared-everything-threads/array.wast/46.print
new file mode 100644
index 00000000..13dd1118
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/46.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.or seq_cst $a
+  )
+  (export "array-atomic-rmw.or-i64-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/47.print b/tests/snapshots/local/shared-everything-threads/array.wast/47.print
new file mode 100644
index 00000000..ffca25c4
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/47.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.or acq_rel $a
+  )
+  (export "array-atomic-rmw.or-i32-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/48.print b/tests/snapshots/local/shared-everything-threads/array.wast/48.print
new file mode 100644
index 00000000..94fbc49c
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/48.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.or acq_rel $a
+  )
+  (export "array-atomic-rmw.or-i64-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/49.print b/tests/snapshots/local/shared-everything-threads/array.wast/49.print
new file mode 100644
index 00000000..0e6016a7
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/49.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xor seq_cst $a
+  )
+  (export "array-atomic-rmw.xor-i32-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/50.print b/tests/snapshots/local/shared-everything-threads/array.wast/50.print
new file mode 100644
index 00000000..446233a0
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/50.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xor seq_cst $a
+  )
+  (export "array-atomic-rmw.xor-i64-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/51.print b/tests/snapshots/local/shared-everything-threads/array.wast/51.print
new file mode 100644
index 00000000..78670261
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/51.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xor acq_rel $a
+  )
+  (export "array-atomic-rmw.xor-i32-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/52.print b/tests/snapshots/local/shared-everything-threads/array.wast/52.print
new file mode 100644
index 00000000..1e8eb5b9
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/52.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xor acq_rel $a
+  )
+  (export "array-atomic-rmw.xor-i64-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/53.print b/tests/snapshots/local/shared-everything-threads/array.wast/53.print
new file mode 100644
index 00000000..71e2c7a8
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/53.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg seq_cst $a
+  )
+  (export "array-atomic-rmw.xchg-i32-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/54.print b/tests/snapshots/local/shared-everything-threads/array.wast/54.print
new file mode 100644
index 00000000..1770c968
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/54.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg seq_cst $a
+  )
+  (export "array-atomic-rmw.xchg-i64-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/55.print b/tests/snapshots/local/shared-everything-threads/array.wast/55.print
new file mode 100644
index 00000000..6ca3ea80
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/55.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut (ref null (shared any))))))
+  (type (;1;) (func (param (ref null $a) i32 (ref null (shared any))) (result (ref null (shared any)))))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg seq_cst $a
+  )
+  (export "array-atomic-rmw.xchg-anyref-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/56.print b/tests/snapshots/local/shared-everything-threads/array.wast/56.print
new file mode 100644
index 00000000..2a03171e
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/56.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg acq_rel $a
+  )
+  (export "array-atomic-rmw.xchg-i32-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/57.print b/tests/snapshots/local/shared-everything-threads/array.wast/57.print
new file mode 100644
index 00000000..61852120
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/57.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg acq_rel $a
+  )
+  (export "array-atomic-rmw.xchg-i64-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/58.print b/tests/snapshots/local/shared-everything-threads/array.wast/58.print
new file mode 100644
index 00000000..2670aba6
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/58.print
@@ -0,0 +1,11 @@
+(module
+  (type $a (;0;) (shared(array (mut (ref null (shared any))))))
+  (type (;1;) (func (param (ref null $a) i32 (ref null (shared any))) (result (ref null (shared any)))))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    local.get $z
+    array.atomic.rmw.xchg acq_rel $a
+  )
+  (export "array-atomic-rmw.xchg-anyref-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/59.print b/tests/snapshots/local/shared-everything-threads/array.wast/59.print
new file mode 100644
index 00000000..9b4ed0ec
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/59.print
@@ -0,0 +1,12 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (param $A i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg seq_cst $a
+  )
+  (export "array-atomic-rmw.cmpxchg-i32-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/60.print b/tests/snapshots/local/shared-everything-threads/array.wast/60.print
new file mode 100644
index 00000000..f485f7a7
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/60.print
@@ -0,0 +1,12 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (param $A i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg seq_cst $a
+  )
+  (export "array-atomic-rmw.cmpxchg-i64-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/61.print b/tests/snapshots/local/shared-everything-threads/array.wast/61.print
new file mode 100644
index 00000000..c9be7ed8
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/61.print
@@ -0,0 +1,12 @@
+(module
+  (type $a (;0;) (shared(array (mut (ref null (shared eq))))))
+  (type (;1;) (func (param (ref null $a) i32 (ref null (shared eq)) (ref null (shared eq))) (result (ref null (shared eq)))))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared eq))) (param $A (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg seq_cst $a
+  )
+  (export "array-atomic-rmw.cmpxchg-eqref-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/62.print b/tests/snapshots/local/shared-everything-threads/array.wast/62.print
new file mode 100644
index 00000000..15e7ca30
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/62.print
@@ -0,0 +1,12 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32 i32 i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i32) (param $A i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg acq_rel $a
+  )
+  (export "array-atomic-rmw.cmpxchg-i32-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/63.print b/tests/snapshots/local/shared-everything-threads/array.wast/63.print
new file mode 100644
index 00000000..7bce20ed
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/63.print
@@ -0,0 +1,12 @@
+(module
+  (type $a (;0;) (shared(array (mut i64))))
+  (type (;1;) (func (param (ref null $a) i32 i64 i64) (result i64)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z i64) (param $A i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg acq_rel $a
+  )
+  (export "array-atomic-rmw.cmpxchg-i64-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/64.print b/tests/snapshots/local/shared-everything-threads/array.wast/64.print
new file mode 100644
index 00000000..d6118598
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/64.print
@@ -0,0 +1,12 @@
+(module
+  (type $a (;0;) (shared(array (mut (ref null (shared eq))))))
+  (type (;1;) (func (param (ref null $a) i32 (ref null (shared eq)) (ref null (shared eq))) (result (ref null (shared eq)))))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (param $z (ref null (shared eq))) (param $A (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    local.get $A
+    array.atomic.rmw.cmpxchg acq_rel $a
+  )
+  (export "array-atomic-rmw.cmpxchg-eqref-acq_rel" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/9.print b/tests/snapshots/local/shared-everything-threads/array.wast/9.print
new file mode 100644
index 00000000..f3d308bd
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/9.print
@@ -0,0 +1,10 @@
+(module
+  (type $a (;0;) (shared(array (mut i32))))
+  (type (;1;) (func (param (ref null $a) i32) (result i32)))
+  (func (;0;) (type 1) (param $x (ref null $a)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    array.atomic.get seq_cst $a
+  )
+  (export "array-atomic-get-i32-seq_cst" (func 0))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast.json b/tests/snapshots/local/shared-everything-threads/struct.wast.json
index 97900a0e..106b265c 100644
--- a/tests/snapshots/local/shared-everything-threads/struct.wast.json
+++ b/tests/snapshots/local/shared-everything-threads/struct.wast.json
@@ -55,6 +55,102 @@
       "type": "module",
       "line": 72,
       "filename": "struct.8.wasm"
+    },
+    {
+      "type": "module",
+      "line": 95,
+      "filename": "struct.9.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 322,
+      "filename": "struct.10.wasm",
+      "text": "non-packed storage type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 331,
+      "filename": "struct.11.wasm",
+      "text": "non-packed storage types",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 340,
+      "filename": "struct.12.wasm",
+      "text": "non-packed storage types",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 349,
+      "filename": "struct.13.wasm",
+      "text": "non-packed storage types",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 358,
+      "filename": "struct.14.wasm",
+      "text": "non-packed storage types",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 367,
+      "filename": "struct.15.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 377,
+      "filename": "struct.16.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 387,
+      "filename": "struct.17.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 397,
+      "filename": "struct.18.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 407,
+      "filename": "struct.19.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 417,
+      "filename": "struct.20.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 427,
+      "filename": "struct.21.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 438,
+      "filename": "struct.22.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
     }
   ]
 }
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast/9.print b/tests/snapshots/local/shared-everything-threads/struct.wast/9.print
new file mode 100644
index 00000000..7987e5e4
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/struct.wast/9.print
@@ -0,0 +1,343 @@
+(module
+  (type $s (;0;) (shared(struct (field $i8 (mut i8)) (field $i16 (mut i16)) (field $i32 (mut i32)) (field $i64 (mut i64)) (field $anyref (mut (ref null (shared any)))) (field $eqref (mut (ref null (shared eq)))))))
+  (type (;1;) (func (param (ref null $s)) (result i32)))
+  (type (;2;) (func (param (ref null $s)) (result i64)))
+  (type (;3;) (func (param (ref null $s)) (result (ref null (shared any)))))
+  (type (;4;) (func (param (ref null $s) i32)))
+  (type (;5;) (func (param (ref null $s) i64)))
+  (type (;6;) (func (param (ref null $s) (ref null (shared any)))))
+  (type (;7;) (func (param (ref null $s) i32) (result i32)))
+  (type (;8;) (func (param (ref null $s) i64) (result i64)))
+  (type (;9;) (func (param (ref null $s) (ref null (shared any))) (result (ref null (shared any)))))
+  (type (;10;) (func (param (ref null $s) i32 i32) (result i32)))
+  (type (;11;) (func (param (ref null $s) i64 i64) (result i64)))
+  (type (;12;) (func (param (ref null $s) (ref null (shared eq)) (ref null (shared eq))) (result (ref null (shared eq)))))
+  (func (;0;) (type 1) (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get seq_cst $s $i32
+  )
+  (func (;1;) (type 2) (param $x (ref null $s)) (result i64)
+    local.get $x
+    struct.atomic.get seq_cst $s $i64
+  )
+  (func (;2;) (type 3) (param $x (ref null $s)) (result (ref null (shared any)))
+    local.get $x
+    struct.atomic.get seq_cst $s $anyref
+  )
+  (func (;3;) (type 1) (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get acq_rel $s $i32
+  )
+  (func (;4;) (type 2) (param $x (ref null $s)) (result i64)
+    local.get $x
+    struct.atomic.get acq_rel $s $i64
+  )
+  (func (;5;) (type 3) (param $x (ref null $s)) (result (ref null (shared any)))
+    local.get $x
+    struct.atomic.get acq_rel $s $anyref
+  )
+  (func (;6;) (type 1) (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_s seq_cst $s $i8
+  )
+  (func (;7;) (type 1) (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_s seq_cst $s $i16
+  )
+  (func (;8;) (type 1) (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_s acq_rel $s $i8
+  )
+  (func (;9;) (type 1) (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_s acq_rel $s $i16
+  )
+  (func (;10;) (type 1) (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_u seq_cst $s $i8
+  )
+  (func (;11;) (type 1) (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_u seq_cst $s $i16
+  )
+  (func (;12;) (type 1) (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_u acq_rel $s $i8
+  )
+  (func (;13;) (type 1) (param $x (ref null $s)) (result i32)
+    local.get $x
+    struct.atomic.get_u acq_rel $s $i16
+  )
+  (func (;14;) (type 4) (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set seq_cst $s $i8
+  )
+  (func (;15;) (type 4) (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set seq_cst $s $i16
+  )
+  (func (;16;) (type 4) (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set seq_cst $s $i32
+  )
+  (func (;17;) (type 5) (param $x (ref null $s)) (param $y i64)
+    local.get $x
+    local.get $y
+    struct.atomic.set seq_cst $s $i64
+  )
+  (func (;18;) (type 6) (param $x (ref null $s)) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.set seq_cst $s $anyref
+  )
+  (func (;19;) (type 4) (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set acq_rel $s $i8
+  )
+  (func (;20;) (type 4) (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set acq_rel $s $i16
+  )
+  (func (;21;) (type 4) (param $x (ref null $s)) (param $y i32)
+    local.get $x
+    local.get $y
+    struct.atomic.set acq_rel $s $i32
+  )
+  (func (;22;) (type 5) (param $x (ref null $s)) (param $y i64)
+    local.get $x
+    local.get $y
+    struct.atomic.set acq_rel $s $i64
+  )
+  (func (;23;) (type 6) (param $x (ref null $s)) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.set acq_rel $s $anyref
+  )
+  (func (;24;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.add seq_cst $s $i32
+  )
+  (func (;25;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.add seq_cst $s $i64
+  )
+  (func (;26;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.add acq_rel $s $i32
+  )
+  (func (;27;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.add acq_rel $s $i64
+  )
+  (func (;28;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.sub seq_cst $s $i32
+  )
+  (func (;29;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.sub seq_cst $s $i64
+  )
+  (func (;30;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.sub acq_rel $s $i32
+  )
+  (func (;31;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.sub acq_rel $s $i64
+  )
+  (func (;32;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.and seq_cst $s $i32
+  )
+  (func (;33;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.and seq_cst $s $i64
+  )
+  (func (;34;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.and acq_rel $s $i32
+  )
+  (func (;35;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.and acq_rel $s $i64
+  )
+  (func (;36;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.or seq_cst $s $i32
+  )
+  (func (;37;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.or seq_cst $s $i64
+  )
+  (func (;38;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.or acq_rel $s $i32
+  )
+  (func (;39;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.or acq_rel $s $i64
+  )
+  (func (;40;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xor seq_cst $s $i32
+  )
+  (func (;41;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xor seq_cst $s $i64
+  )
+  (func (;42;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xor acq_rel $s $i32
+  )
+  (func (;43;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xor acq_rel $s $i64
+  )
+  (func (;44;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg seq_cst $s $i32
+  )
+  (func (;45;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg seq_cst $s $i64
+  )
+  (func (;46;) (type 9) (param $x (ref null $s)) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg seq_cst $s $anyref
+  )
+  (func (;47;) (type 7) (param $x (ref null $s)) (param $y i32) (result i32)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg acq_rel $s $i32
+  )
+  (func (;48;) (type 8) (param $x (ref null $s)) (param $y i64) (result i64)
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg acq_rel $s $i64
+  )
+  (func (;49;) (type 9) (param $x (ref null $s)) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    struct.atomic.rmw.xchg acq_rel $s $anyref
+  )
+  (func (;50;) (type 10) (param $x (ref null $s)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg seq_cst $s $i32
+  )
+  (func (;51;) (type 11) (param $x (ref null $s)) (param $y i64) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg seq_cst $s $i64
+  )
+  (func (;52;) (type 12) (param $x (ref null $s)) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg seq_cst $s $eqref
+  )
+  (func (;53;) (type 10) (param $x (ref null $s)) (param $y i32) (param $z i32) (result i32)
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg acq_rel $s $i32
+  )
+  (func (;54;) (type 11) (param $x (ref null $s)) (param $y i64) (param $z i64) (result i64)
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg acq_rel $s $i64
+  )
+  (func (;55;) (type 12) (param $x (ref null $s)) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    struct.atomic.rmw.cmpxchg acq_rel $s $eqref
+  )
+  (export "struct-atomic-get-i32-seq_cst" (func 0))
+  (export "struct-atomic-get-i64-seq_cst" (func 1))
+  (export "struct-atomic-get-anyref-seq_cst" (func 2))
+  (export "struct-atomic-get-i32-acq_rel" (func 3))
+  (export "struct-atomic-get-i64-acq_rel" (func 4))
+  (export "struct-atomic-get-anyref-acq_rel" (func 5))
+  (export "struct-atomic-get_s-i8-seq_cst" (func 6))
+  (export "struct-atomic-get_s-i16-seq_cst" (func 7))
+  (export "struct-atomic-get_s-i8-acq_rel" (func 8))
+  (export "struct-atomic-get_s-i16-acq_rel" (func 9))
+  (export "struct-atomic-get_u-i8-seq_cst" (func 10))
+  (export "struct-atomic-get_u-i16-seq_cst" (func 11))
+  (export "struct-atomic-get_u-i8-acq_rel" (func 12))
+  (export "struct-atomic-get_u-i16-acq_rel" (func 13))
+  (export "struct-atomic-set-i8-seq_cst" (func 14))
+  (export "struct-atomic-set-i16-seq_cst" (func 15))
+  (export "struct-atomic-set-i32-seq_cst" (func 16))
+  (export "struct-atomic-set-i64-seq_cst" (func 17))
+  (export "struct-atomic-set-anyref-seq_cst" (func 18))
+  (export "struct-atomic-set-i8-acq_rel" (func 19))
+  (export "struct-atomic-set-i16-acq_rel" (func 20))
+  (export "struct-atomic-set-i32-acq_rel" (func 21))
+  (export "struct-atomic-set-i64-acq_rel" (func 22))
+  (export "struct-atomic-set-anyref-acq_rel" (func 23))
+  (export "struct-atomic-rmw.add-i32-seq_cst" (func 24))
+  (export "struct-atomic-rmw.add-i64-seq_cst" (func 25))
+  (export "struct-atomic-rmw.add-i32-acq_rel" (func 26))
+  (export "struct-atomic-rmw.add-i64-acq_rel" (func 27))
+  (export "struct-atomic-rmw.sub-i32-seq_cst" (func 28))
+  (export "struct-atomic-rmw.sub-i64-seq_cst" (func 29))
+  (export "struct-atomic-rmw.sub-i32-acq_rel" (func 30))
+  (export "struct-atomic-rmw.sub-i64-acq_rel" (func 31))
+  (export "struct-atomic-rmw.and-i32-seq_cst" (func 32))
+  (export "struct-atomic-rmw.and-i64-seq_cst" (func 33))
+  (export "struct-atomic-rmw.and-i32-acq_rel" (func 34))
+  (export "struct-atomic-rmw.and-i64-acq_rel" (func 35))
+  (export "struct-atomic-rmw.or-i32-seq_cst" (func 36))
+  (export "struct-atomic-rmw.or-i64-seq_cst" (func 37))
+  (export "struct-atomic-rmw.or-i32-acq_rel" (func 38))
+  (export "struct-atomic-rmw.or-i64-acq_rel" (func 39))
+  (export "struct-atomic-rmw.xor-i32-seq_cst" (func 40))
+  (export "struct-atomic-rmw.xor-i64-seq_cst" (func 41))
+  (export "struct-atomic-rmw.xor-i32-acq_rel" (func 42))
+  (export "struct-atomic-rmw.xor-i64-acq_rel" (func 43))
+  (export "struct-atomic-rmw.xchg-i32-seq_cst" (func 44))
+  (export "struct-atomic-rmw.xchg-i64-seq_cst" (func 45))
+  (export "struct-atomic-rmw.xchg-anyref-seq_cst" (func 46))
+  (export "struct-atomic-rmw.xchg-i32-acq_rel" (func 47))
+  (export "struct-atomic-rmw.xchg-i64-acq_rel" (func 48))
+  (export "struct-atomic-rmw.xchg-anyref-acq_rel" (func 49))
+  (export "struct-atomic-rmw.cmpxchg-i32-seq_cst" (func 50))
+  (export "struct-atomic-rmw.cmpxchg-i64-seq_cst" (func 51))
+  (export "struct-atomic-rmw.cmpxchg-eqref-seq_cst" (func 52))
+  (export "struct-atomic-rmw.cmpxchg-i32-acq_rel" (func 53))
+  (export "struct-atomic-rmw.cmpxchg-i64-acq_rel" (func 54))
+  (export "struct-atomic-rmw.cmpxchg-eqref-acq_rel" (func 55))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/table.wast.json b/tests/snapshots/local/shared-everything-threads/table.wast.json
index 8c2bc6b1..13a852f3 100644
--- a/tests/snapshots/local/shared-everything-threads/table.wast.json
+++ b/tests/snapshots/local/shared-everything-threads/table.wast.json
@@ -45,6 +45,23 @@
       "filename": "table.6.wasm",
       "text": "shared tables must have a shared element type",
       "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 46,
+      "filename": "table.7.wasm"
+    },
+    {
+      "type": "module",
+      "line": 115,
+      "filename": "table.8.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 167,
+      "filename": "table.9.wasm",
+      "text": "invalid type",
+      "module_type": "binary"
     }
   ]
 }
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/table.wast/7.print b/tests/snapshots/local/shared-everything-threads/table.wast/7.print
new file mode 100644
index 00000000..033089a8
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/table.wast/7.print
@@ -0,0 +1,104 @@
+(module
+  (type (;0;) (func (param i32) (result (ref null (shared eq)))))
+  (type (;1;) (func (param i32 (ref null (shared eq)))))
+  (type (;2;) (func (param i32 (ref null (shared eq))) (result (ref null (shared eq)))))
+  (type (;3;) (func (param i32 (ref null (shared eq)) (ref null (shared eq))) (result (ref null (shared eq)))))
+  (import "spectest" "table_eq" (table $a (;0;) shared 1 (ref null (shared eq))))
+  (func (;0;) (type 0) (param $x i32) (result (ref null (shared eq)))
+    local.get $x
+    table.atomic.get seq_cst $a
+  )
+  (func (;1;) (type 0) (param $x i32) (result (ref null (shared eq)))
+    local.get $x
+    table.atomic.get seq_cst $b
+  )
+  (func (;2;) (type 0) (param $x i32) (result (ref null (shared eq)))
+    local.get $x
+    table.atomic.get acq_rel $a
+  )
+  (func (;3;) (type 0) (param $x i32) (result (ref null (shared eq)))
+    local.get $x
+    table.atomic.get acq_rel $b
+  )
+  (func (;4;) (type 1) (param $x i32) (param $y (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.set seq_cst $a
+  )
+  (func (;5;) (type 1) (param $x i32) (param $y (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.set seq_cst $b
+  )
+  (func (;6;) (type 1) (param $x i32) (param $y (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.set acq_rel $a
+  )
+  (func (;7;) (type 1) (param $x i32) (param $y (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.set acq_rel $b
+  )
+  (func (;8;) (type 2) (param $x i32) (param $y (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg seq_cst $a
+  )
+  (func (;9;) (type 2) (param $x i32) (param $y (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg seq_cst $b
+  )
+  (func (;10;) (type 2) (param $x i32) (param $y (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg acq_rel $a
+  )
+  (func (;11;) (type 2) (param $x i32) (param $y (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg acq_rel $b
+  )
+  (func (;12;) (type 3) (param $x i32) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    table.atomic.rmw.cmpxchg seq_cst $a
+  )
+  (func (;13;) (type 3) (param $x i32) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    table.atomic.rmw.cmpxchg seq_cst $b
+  )
+  (func (;14;) (type 3) (param $x i32) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    table.atomic.rmw.cmpxchg acq_rel $a
+  )
+  (func (;15;) (type 3) (param $x i32) (param $y (ref null (shared eq))) (param $z (ref null (shared eq))) (result (ref null (shared eq)))
+    local.get $x
+    local.get $y
+    local.get $z
+    table.atomic.rmw.cmpxchg acq_rel $b
+  )
+  (table $b (;1;) shared 1 (ref null (shared eq)))
+  (export "table-atomic-get-eq-seq_cst-$a" (func 0))
+  (export "table-atomic-get-eq-seq_cst-$b" (func 1))
+  (export "table-atomic-get-eq-acq_rel-$a" (func 2))
+  (export "table-atomic-get-eq-acq_rel-$b" (func 3))
+  (export "table-atomic-set-eq-seq_cst-$a" (func 4))
+  (export "table-atomic-set-eq-seq_cst-$b" (func 5))
+  (export "table-atomic-set-eq-acq_rel-$a" (func 6))
+  (export "table-atomic-set-eq-acq_rel-$b" (func 7))
+  (export "table-atomic-rmw.xchg-eq-seq_cst-$a" (func 8))
+  (export "table-atomic-rmw.xchg-eq-seq_cst-$b" (func 9))
+  (export "table-atomic-rmw.xchg-eq-acq_rel-$a" (func 10))
+  (export "table-atomic-rmw.xchg-eq-acq_rel-$b" (func 11))
+  (export "table-atomic-rmw.cmpxchg-eq-seq_cst-$a" (func 12))
+  (export "table-atomic-rmw.cmpxchg-eq-seq_cst-$b" (func 13))
+  (export "table-atomic-rmw.cmpxchg-eq-acq_rel-$a" (func 14))
+  (export "table-atomic-rmw.cmpxchg-eq-acq_rel-$b" (func 15))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/table.wast/8.print b/tests/snapshots/local/shared-everything-threads/table.wast/8.print
new file mode 100644
index 00000000..e921f4e8
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/table.wast/8.print
@@ -0,0 +1,75 @@
+(module
+  (type (;0;) (func (param i32) (result (ref null (shared any)))))
+  (type (;1;) (func (param i32 (ref null (shared any)))))
+  (type (;2;) (func (param i32 (ref null (shared any))) (result (ref null (shared any)))))
+  (import "spectest" "table_any" (table $a (;0;) shared 1 (ref null (shared any))))
+  (func (;0;) (type 0) (param $x i32) (result (ref null (shared any)))
+    local.get $x
+    table.atomic.get seq_cst $a
+  )
+  (func (;1;) (type 0) (param $x i32) (result (ref null (shared any)))
+    local.get $x
+    table.atomic.get seq_cst $b
+  )
+  (func (;2;) (type 0) (param $x i32) (result (ref null (shared any)))
+    local.get $x
+    table.atomic.get acq_rel $a
+  )
+  (func (;3;) (type 0) (param $x i32) (result (ref null (shared any)))
+    local.get $x
+    table.atomic.get acq_rel $b
+  )
+  (func (;4;) (type 1) (param $x i32) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.set seq_cst $a
+  )
+  (func (;5;) (type 1) (param $x i32) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.set seq_cst $b
+  )
+  (func (;6;) (type 1) (param $x i32) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.set acq_rel $a
+  )
+  (func (;7;) (type 1) (param $x i32) (param $y (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.set acq_rel $b
+  )
+  (func (;8;) (type 2) (param $x i32) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg seq_cst $a
+  )
+  (func (;9;) (type 2) (param $x i32) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg seq_cst $b
+  )
+  (func (;10;) (type 2) (param $x i32) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg acq_rel $a
+  )
+  (func (;11;) (type 2) (param $x i32) (param $y (ref null (shared any))) (result (ref null (shared any)))
+    local.get $x
+    local.get $y
+    table.atomic.rmw.xchg acq_rel $b
+  )
+  (table $b (;1;) shared 1 (ref null (shared any)))
+  (export "table-atomic-get-any-seq_cst-$a" (func 0))
+  (export "table-atomic-get-any-seq_cst-$b" (func 1))
+  (export "table-atomic-get-any-acq_rel-$a" (func 2))
+  (export "table-atomic-get-any-acq_rel-$b" (func 3))
+  (export "table-atomic-set-any-seq_cst-$a" (func 4))
+  (export "table-atomic-set-any-seq_cst-$b" (func 5))
+  (export "table-atomic-set-any-acq_rel-$a" (func 6))
+  (export "table-atomic-set-any-acq_rel-$b" (func 7))
+  (export "table-atomic-rmw.xchg-any-seq_cst-$a" (func 8))
+  (export "table-atomic-rmw.xchg-any-seq_cst-$b" (func 9))
+  (export "table-atomic-rmw.xchg-any-acq_rel-$a" (func 10))
+  (export "table-atomic-rmw.xchg-any-acq_rel-$b" (func 11))
+)