Repository: bytecodealliance/wasm-tools
Pull request #1584 (2024-05-29 17:33:43)

Remove mem_byte/table_byte instruction payloads
This commit removes a workaround in the `wasmparser` opcode representation. The `mem_byte` field of `MemoryGrow` and `MemorySize` and the `table_byte` field of `CallIndirect` are no longer necessary after #1548 because features can be consulted directly when parsing binaries.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index eb624f83..0812c56c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1480,7 +1480,7 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-compose"
-version = "0.209.0"
+version = "0.209.1"
 dependencies = [
  "anyhow",
  "glob",
@@ -1494,9 +1494,9 @@ dependencies = [
  "serde_derive",
  "serde_yaml",
  "smallvec",
- "wasm-encoder 0.209.0",
- "wasmparser 0.209.0",
- "wasmprinter 0.209.0",
+ "wasm-encoder 0.209.1",
+ "wasmparser 0.209.1",
+ "wasmprinter 0.209.1",
  "wat",
  "wit-component",
 ]
@@ -1512,17 +1512,17 @@ dependencies = [
 
 [[package]]
 name = "wasm-encoder"
-version = "0.209.0"
+version = "0.209.1"
 dependencies = [
  "anyhow",
  "leb128",
  "tempfile",
- "wasmparser 0.209.0",
+ "wasmparser 0.209.1",
 ]
 
 [[package]]
 name = "wasm-metadata"
-version = "0.209.0"
+version = "0.209.1"
 dependencies = [
  "anyhow",
  "clap",
@@ -1531,14 +1531,14 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "spdx",
- "wasm-encoder 0.209.0",
- "wasmparser 0.209.0",
+ "wasm-encoder 0.209.1",
+ "wasmparser 0.209.1",
  "wat",
 ]
 
 [[package]]
 name = "wasm-mutate"
-version = "0.209.0"
+version = "0.209.1"
 dependencies = [
  "anyhow",
  "clap",
@@ -1547,9 +1547,9 @@ dependencies = [
  "log",
  "rand",
  "thiserror",
- "wasm-encoder 0.209.0",
- "wasmparser 0.209.0",
- "wasmprinter 0.209.0",
+ "wasm-encoder 0.209.1",
+ "wasmparser 0.209.1",
+ "wasmprinter 0.209.1",
  "wat",
 ]
 
@@ -1566,14 +1566,14 @@ dependencies = [
  "num_cpus",
  "rand",
  "wasm-mutate",
- "wasmparser 0.209.0",
- "wasmprinter 0.209.0",
+ "wasmparser 0.209.1",
+ "wasmprinter 0.209.1",
  "wasmtime",
 ]
 
 [[package]]
 name = "wasm-shrink"
-version = "0.209.0"
+version = "0.209.1"
 dependencies = [
  "anyhow",
  "blake3",
@@ -1582,14 +1582,14 @@ dependencies = [
  "log",
  "rand",
  "wasm-mutate",
- "wasmparser 0.209.0",
- "wasmprinter 0.209.0",
+ "wasmparser 0.209.1",
+ "wasmprinter 0.209.1",
  "wat",
 ]
 
 [[package]]
 name = "wasm-smith"
-version = "0.209.0"
+version = "0.209.1"
 dependencies = [
  "anyhow",
  "arbitrary",
@@ -1602,15 +1602,15 @@ dependencies = [
  "rand",
  "serde",
  "serde_derive",
- "wasm-encoder 0.209.0",
- "wasmparser 0.209.0",
- "wasmprinter 0.209.0",
+ "wasm-encoder 0.209.1",
+ "wasmparser 0.209.1",
+ "wasmprinter 0.209.1",
  "wat",
 ]
 
 [[package]]
 name = "wasm-tools"
-version = "1.209.0"
+version = "1.209.1"
 dependencies = [
  "addr2line",
  "anyhow",
@@ -1634,17 +1634,17 @@ dependencies = [
  "tempfile",
  "termcolor",
  "wasm-compose",
- "wasm-encoder 0.209.0",
+ "wasm-encoder 0.209.1",
  "wasm-metadata",
  "wasm-mutate",
  "wasm-shrink",
  "wasm-smith",
- "wasmparser 0.209.0",
- "wasmprinter 0.209.0",
+ "wasmparser 0.209.1",
+ "wasmprinter 0.209.1",
  "wast",
  "wat",
  "wit-component",
- "wit-parser 0.209.0",
+ "wit-parser 0.209.1",
  "wit-smith",
 ]
 
@@ -1656,8 +1656,8 @@ dependencies = [
  "wasm-mutate",
  "wasm-shrink",
  "wasm-smith",
- "wasmparser 0.209.0",
- "wasmprinter 0.209.0",
+ "wasmparser 0.209.1",
+ "wasmprinter 0.209.1",
  "wast",
  "wat",
 ]
@@ -1672,16 +1672,16 @@ dependencies = [
  "libfuzzer-sys",
  "log",
  "tempfile",
- "wasm-encoder 0.209.0",
+ "wasm-encoder 0.209.1",
  "wasm-mutate",
  "wasm-smith",
- "wasmparser 0.209.0",
- "wasmprinter 0.209.0",
+ "wasmparser 0.209.1",
+ "wasmprinter 0.209.1",
  "wasmtime",
  "wast",
  "wat",
  "wit-component",
- "wit-parser 0.209.0",
+ "wit-parser 0.209.1",
  "wit-smith",
 ]
 
@@ -1698,7 +1698,7 @@ dependencies = [
 
 [[package]]
 name = "wasmparser"
-version = "0.209.0"
+version = "0.209.1"
 dependencies = [
  "ahash",
  "anyhow",
@@ -1712,7 +1712,7 @@ dependencies = [
  "rayon",
  "semver",
  "serde",
- "wasm-encoder 0.209.0",
+ "wasm-encoder 0.209.1",
  "wast",
  "wat",
 ]
@@ -1729,13 +1729,13 @@ dependencies = [
 
 [[package]]
 name = "wasmprinter"
-version = "0.209.0"
+version = "0.209.1"
 dependencies = [
  "anyhow",
  "diff",
  "rayon",
  "tempfile",
- "wasmparser 0.209.0",
+ "wasmparser 0.209.1",
  "wast",
  "wat",
 ]
@@ -1953,7 +1953,7 @@ dependencies = [
 
 [[package]]
 name = "wast"
-version = "209.0.0"
+version = "209.0.1"
 dependencies = [
  "anyhow",
  "bumpalo",
@@ -1961,14 +1961,14 @@ dependencies = [
  "libtest-mimic",
  "memchr",
  "unicode-width",
- "wasm-encoder 0.209.0",
- "wasmparser 0.209.0",
+ "wasm-encoder 0.209.1",
+ "wasmparser 0.209.1",
  "wat",
 ]
 
 [[package]]
 name = "wat"
-version = "1.209.0"
+version = "1.209.1"
 dependencies = [
  "wast",
 ]
@@ -2096,7 +2096,7 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
 
 [[package]]
 name = "wit-component"
-version = "0.209.0"
+version = "0.209.1"
 dependencies = [
  "anyhow",
  "bitflags",
@@ -2109,14 +2109,14 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "wasm-encoder 0.209.0",
+ "wasm-encoder 0.209.1",
  "wasm-metadata",
- "wasmparser 0.209.0",
- "wasmprinter 0.209.0",
+ "wasmparser 0.209.1",
+ "wasmprinter 0.209.1",
  "wasmtime",
  "wast",
  "wat",
- "wit-parser 0.209.0",
+ "wit-parser 0.209.1",
 ]
 
 [[package]]
@@ -2139,7 +2139,7 @@ dependencies = [
 
 [[package]]
 name = "wit-parser"
-version = "0.209.0"
+version = "0.209.1"
 dependencies = [
  "anyhow",
  "env_logger",
@@ -2153,9 +2153,9 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "unicode-xid",
- "wasmparser 0.209.0",
+ "wasmparser 0.209.1",
  "wat",
- "wit-parser 0.209.0",
+ "wit-parser 0.209.1",
 ]
 
 [[package]]
@@ -2166,13 +2166,13 @@ dependencies = [
  "env_logger",
  "libfuzzer-sys",
  "log",
- "wasmprinter 0.209.0",
- "wit-parser 0.209.0",
+ "wasmprinter 0.209.1",
+ "wit-parser 0.209.1",
 ]
 
 [[package]]
 name = "wit-smith"
-version = "0.209.0"
+version = "0.209.1"
 dependencies = [
  "arbitrary",
  "clap",
@@ -2180,7 +2180,7 @@ dependencies = [
  "log",
  "semver",
  "wit-component",
- "wit-parser 0.209.0",
+ "wit-parser 0.209.1",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 2c6ea24a..22704755 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "wasm-tools"
-version = "1.209.0"
+version = "1.209.1"
 authors = ["The Wasmtime Project Developers"]
 edition.workspace = true
 description = "CLI tools for interoperating with WebAssembly files"
@@ -49,7 +49,7 @@ all = "allow"
 
 [workspace.package]
 edition = '2021'
-version = "0.209.0"
+version = "0.209.1"
 # Current policy for wasm-tools is the same as Wasmtime which is that this
 # number can be no larger than the current stable release of Rust minus 2.
 rust-version = "1.76.0"
@@ -81,19 +81,19 @@ bitflags = "2.5.0"
 hashbrown = { version = "0.14.3", default-features = false, features = ['ahash'] }
 ahash = { version = "0.8.11", default-features = false }
 
-wasm-compose = { version = "0.209.0", path = "crates/wasm-compose" }
-wasm-encoder = { version = "0.209.0", path = "crates/wasm-encoder" }
-wasm-metadata = { version = "0.209.0", path = "crates/wasm-metadata" }
-wasm-mutate = { version = "0.209.0", path = "crates/wasm-mutate" }
-wasm-shrink = { version = "0.209.0", path = "crates/wasm-shrink" }
-wasm-smith = { version = "0.209.0", path = "crates/wasm-smith" }
-wasmparser = { version = "0.209.0", path = "crates/wasmparser", default-features = false, features = ['std'] }
-wasmprinter = { version = "0.209.0", path = "crates/wasmprinter" }
-wast = { version = "209.0.0", path = "crates/wast" }
-wat = { version = "1.209.0", path = "crates/wat" }
-wit-component = { version = "0.209.0", path = "crates/wit-component" }
-wit-parser = { version = "0.209.0", path = "crates/wit-parser" }
-wit-smith = { version = "0.209.0", path = "crates/wit-smith" }
+wasm-compose = { version = "0.209.1", path = "crates/wasm-compose" }
+wasm-encoder = { version = "0.209.1", path = "crates/wasm-encoder" }
+wasm-metadata = { version = "0.209.1", path = "crates/wasm-metadata" }
+wasm-mutate = { version = "0.209.1", path = "crates/wasm-mutate" }
+wasm-shrink = { version = "0.209.1", path = "crates/wasm-shrink" }
+wasm-smith = { version = "0.209.1", path = "crates/wasm-smith" }
+wasmparser = { version = "0.209.1", path = "crates/wasmparser", default-features = false, features = ['std'] }
+wasmprinter = { version = "0.209.1", path = "crates/wasmprinter" }
+wast = { version = "209.0.1", path = "crates/wast" }
+wat = { version = "1.209.1", path = "crates/wat" }
+wit-component = { version = "0.209.1", path = "crates/wit-component" }
+wit-parser = { version = "0.209.1", path = "crates/wit-parser" }
+wit-smith = { version = "0.209.1", path = "crates/wit-smith" }
 
 [dependencies]
 anyhow = { workspace = true }
diff --git a/crates/wasm-mutate/src/mutators/peephole/dfg.rs b/crates/wasm-mutate/src/mutators/peephole/dfg.rs
index 1024f113..c69391b3 100644
--- a/crates/wasm-mutate/src/mutators/peephole/dfg.rs
+++ b/crates/wasm-mutate/src/mutators/peephole/dfg.rs
@@ -640,11 +640,11 @@ impl<'a> DFGBuilder {
                         idx,
                     );
                 }
-                Operator::MemoryGrow { mem, mem_byte: _ } => {
+                Operator::MemoryGrow { mem } => {
                     let arg = self.pop_operand(idx, false);
                     self.push_node(Lang::MemoryGrow(*mem, Id::from(arg)), idx);
                 }
-                Operator::MemorySize { mem, mem_byte: _ } => {
+                Operator::MemorySize { mem } => {
                     self.push_node(Lang::MemorySize(*mem), idx);
                 }
                 Operator::TableGrow { table } => {
diff --git a/crates/wasm-mutate/src/mutators/translate.rs b/crates/wasm-mutate/src/mutators/translate.rs
index ee0d7028..5c5fbbb2 100644
--- a/crates/wasm-mutate/src/mutators/translate.rs
+++ b/crates/wasm-mutate/src/mutators/translate.rs
@@ -370,8 +370,6 @@ pub fn op(t: &mut dyn Translator, op: &Operator<'_>) -> Result<Instruction<'stat
                 .into(),
             $arg.default(),
         ));
-        (map $arg:ident table_byte) => (());
-        (map $arg:ident mem_byte) => (());
         (map $arg:ident flags) => (());
         (map $arg:ident ty) => (t.translate_ty($arg)?);
         (map $arg:ident hty) => (t.translate_heapty($arg)?);
@@ -402,7 +400,7 @@ pub fn op(t: &mut dyn Translator, op: &Operator<'_>) -> Result<Instruction<'stat
         (build V128Const $arg:ident) => (I::V128Const($arg.i128()));
         (build TryTable $table:ident) => (unimplemented_try_table());
         (build $op:ident $arg:ident) => (I::$op($arg));
-        (build CallIndirect $ty:ident $table:ident $_:ident) => (I::CallIndirect {
+        (build CallIndirect $ty:ident $table:ident) => (I::CallIndirect {
             ty: $ty,
             table: $table,
         });
@@ -410,14 +408,6 @@ pub fn op(t: &mut dyn Translator, op: &Operator<'_>) -> Result<Instruction<'stat
             ty: $ty,
             table: $table,
         });
-        (build MemoryGrow $mem:ident $_:ident) => (I::MemoryGrow($mem));
-        (build MemorySize $mem:ident $_:ident) => (I::MemorySize($mem));
-        (build StructNew $type_index:ident) => (I::StructNew($type_index));
-        (build ArrayGet $type_index:ident) => (I::ArrayGet($type_index));
-        (build ArrayGetS $type_index:ident) => (I::ArrayGetS($type_index));
-        (build ArrayGetU $type_index:ident) => (I::ArrayGetU($type_index));
-        (build ArraySet $type_index:ident) => (I::ArraySet($type_index));
-        (build ArrayFill $type_index:ident) => (I::ArrayFill($type_index));
         (build $op:ident $($arg:ident)*) => (I::$op { $($arg),* });
     }
 
diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs
index fe059792..e542db1c 100644
--- a/crates/wasmparser/src/binary_reader.rs
+++ b/crates/wasmparser/src/binary_reader.rs
@@ -281,12 +281,6 @@ impl<'a> BinaryReader<'a> {
         })
     }
 
-    fn read_first_byte_and_var_u32(&mut self) -> Result<(u8, u32)> {
-        let pos = self.position;
-        let val = self.read_var_u32()?;
-        Ok((self.buffer[pos], val))
-    }
-
     fn read_memarg(&mut self, max_align: u8) -> Result<MemArg> {
         let flags_pos = self.original_position();
         let mut flags = self.read_var_u32()?;
@@ -833,8 +827,8 @@ impl<'a> BinaryReader<'a> {
             0x10 => visitor.visit_call(self.read_var_u32()?),
             0x11 => {
                 let index = self.read_var_u32()?;
-                let (table_byte, table_index) = self.read_first_byte_and_var_u32()?;
-                visitor.visit_call_indirect(index, table_index, table_byte)
+                let table = self.read_table_index_or_zero_if_not_reference_types()?;
+                visitor.visit_call_indirect(index, table)
             }
             0x12 => visitor.visit_return_call(self.read_var_u32()?),
             0x13 => visitor.visit_return_call_indirect(self.read_var_u32()?, self.read_var_u32()?),
@@ -888,12 +882,12 @@ impl<'a> BinaryReader<'a> {
             0x3d => visitor.visit_i64_store16(self.read_memarg(1)?),
             0x3e => visitor.visit_i64_store32(self.read_memarg(2)?),
             0x3f => {
-                let (mem_byte, mem) = self.read_first_byte_and_var_u32()?;
-                visitor.visit_memory_size(mem, mem_byte)
+                let mem = self.read_memory_index_or_zero_if_not_multi_memory()?;
+                visitor.visit_memory_size(mem)
             }
             0x40 => {
-                let (mem_byte, mem) = self.read_first_byte_and_var_u32()?;
-                visitor.visit_memory_grow(mem, mem_byte)
+                let mem = self.read_memory_index_or_zero_if_not_multi_memory()?;
+                visitor.visit_memory_grow(mem)
             }
 
             0x41 => visitor.visit_i32_const(self.read_var_i32()?),
@@ -1754,6 +1748,32 @@ impl<'a> BinaryReader<'a> {
             }
         }
     }
+
+    fn read_memory_index_or_zero_if_not_multi_memory(&mut self) -> Result<u32> {
+        if self.features.multi_memory() {
+            self.read_var_u32()
+        } else {
+            // Before bulk memory this byte was required to be a single zero
+            // byte, not a LEB-encoded zero, so require a precise zero byte.
+            match self.read_u8()? {
+                0 => Ok(0),
+                _ => bail!(self.original_position() - 1, "zero byte expected"),
+            }
+        }
+    }
+
+    fn read_table_index_or_zero_if_not_reference_types(&mut self) -> Result<u32> {
+        if self.features.reference_types() {
+            self.read_var_u32()
+        } else {
+            // Before reference types this byte was required to be a single zero
+            // byte, not a LEB-encoded zero, so require a precise zero byte.
+            match self.read_u8()? {
+                0 => Ok(0),
+                _ => bail!(self.original_position() - 1, "zero byte expected"),
+            }
+        }
+    }
 }
 
 impl<'a> BrTable<'a> {
diff --git a/crates/wasmparser/src/lib.rs b/crates/wasmparser/src/lib.rs
index 909dbea7..06b1a9ad 100644
--- a/crates/wasmparser/src/lib.rs
+++ b/crates/wasmparser/src/lib.rs
@@ -169,7 +169,7 @@ macro_rules! for_each_operator {
             @mvp BrTable { targets: $crate::BrTable<'a> } => visit_br_table
             @mvp Return => visit_return
             @mvp Call { function_index: u32 } => visit_call
-            @mvp CallIndirect { type_index: u32, table_index: u32, table_byte: u8 } => visit_call_indirect
+            @mvp CallIndirect { type_index: u32, table_index: u32 } => visit_call_indirect
             @tail_call ReturnCall { function_index: u32 } => visit_return_call
             @tail_call ReturnCallIndirect { type_index: u32, table_index: u32 } => visit_return_call_indirect
             @mvp Drop => visit_drop
@@ -203,8 +203,8 @@ macro_rules! for_each_operator {
             @mvp I64Store8 { memarg: $crate::MemArg } => visit_i64_store8
             @mvp I64Store16 { memarg: $crate::MemArg } => visit_i64_store16
             @mvp I64Store32 { memarg: $crate::MemArg } => visit_i64_store32
-            @mvp MemorySize { mem: u32, mem_byte: u8 } => visit_memory_size
-            @mvp MemoryGrow { mem: u32, mem_byte: u8 } => visit_memory_grow
+            @mvp MemorySize { mem: u32 } => visit_memory_size
+            @mvp MemoryGrow { mem: u32 } => visit_memory_grow
             @mvp I32Const { value: i32 } => visit_i32_const
             @mvp I64Const { value: i64 } => visit_i64_const
             @mvp F32Const { value: $crate::Ieee32 } => visit_f32_const
diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs
index a3d1c313..eae9f81e 100644
--- a/crates/wasmparser/src/validator/operators.rs
+++ b/crates/wasmparser/src/validator/operators.rs
@@ -796,18 +796,22 @@ where
         }
     }
 
-    /// Validates a `call` instruction, ensuring that the function index is
-    /// in-bounds and the right types are on the stack to call the function.
-    fn check_call(&mut self, function_index: u32) -> Result<()> {
-        let ty = match self.resources.type_of_function(function_index) {
-            Some(i) => i,
+    fn type_of_function(&self, function_index: u32) -> Result<&'resources FuncType> {
+        match self.resources.type_of_function(function_index) {
+            Some(f) => Ok(f),
             None => {
                 bail!(
                     self.offset,
                     "unknown function {function_index}: function index out of bounds",
-                );
+                )
             }
-        };
+        }
+    }
+
+    /// Validates a `call` instruction, ensuring that the function index is
+    /// in-bounds and the right types are on the stack to call the function.
+    fn check_call(&mut self, function_index: u32) -> Result<()> {
+        let ty = self.type_of_function(function_index)?;
         self.check_call_ty(ty)
     }
 
@@ -864,6 +868,49 @@ where
         Ok(())
     }
 
+    /// Check that the function at the given index has the same result types as
+    /// the current function's results.
+    fn check_func_same_results(&self, function_index: u32) -> Result<()> {
+        let ty = self.type_of_function(function_index)?;
+        self.check_func_type_same_results(ty)
+    }
+
+    /// Check that the type at the given index has the same result types as the
+    /// current function's results.
+    fn check_func_type_index_same_results(&self, type_index: u32) -> Result<()> {
+        let ty = self.func_type_at(type_index)?;
+        self.check_func_type_same_results(ty)
+    }
+
+    /// Check that the given type has the same result types as the current
+    /// function's results.
+    fn check_func_type_same_results(&self, callee_ty: &FuncType) -> Result<()> {
+        let caller_rets = self.results(self.control[0].block_type)?;
+        if callee_ty.results().len() != caller_rets.len()
+            || !caller_rets
+                .zip(callee_ty.results())
+                .all(|(caller_ty, callee_ty)| self.resources.is_subtype(*callee_ty, caller_ty))
+        {
+            let caller_rets = self
+                .results(self.control[0].block_type)?
+                .map(|ty| format!("{ty}"))
+                .collect::<Vec<_>>()
+                .join(" ");
+            let callee_rets = callee_ty
+                .results()
+                .iter()
+                .map(|ty| format!("{ty}"))
+                .collect::<Vec<_>>()
+                .join(" ");
+            bail!(
+                self.offset,
+                "type mismatch: current function requires result type \
+                 [{caller_rets}] but callee returns [{callee_rets}]"
+            );
+        }
+        Ok(())
+    }
+
     /// Checks the validity of a common comparison operator.
     fn check_cmp_op(&mut self, ty: ValType) -> Result<()> {
         self.pop_operand(Some(ty))?;
@@ -1510,6 +1557,7 @@ where
     fn visit_return_call(&mut self, function_index: u32) -> Self::Output {
         self.check_call(function_index)?;
         self.check_return()?;
+        self.check_func_same_results(function_index)?;
         Ok(())
     }
     fn visit_call_ref(&mut self, type_index: u32) -> Self::Output {
@@ -1532,26 +1580,18 @@ where
     }
     fn visit_return_call_ref(&mut self, type_index: u32) -> Self::Output {
         self.visit_call_ref(type_index)?;
-        self.check_return()
+        self.check_return()?;
+        self.check_func_type_index_same_results(type_index)?;
+        Ok(())
     }
-    fn visit_call_indirect(
-        &mut self,
-        index: u32,
-        table_index: u32,
-        table_byte: u8,
-    ) -> Self::Output {
-        if table_byte != 0 && !self.features.reference_types() {
-            bail!(
-                self.offset,
-                "reference-types not enabled: zero byte expected"
-            );
-        }
+    fn visit_call_indirect(&mut self, index: u32, table_index: u32) -> Self::Output {
         self.check_call_indirect(index, table_index)?;
         Ok(())
     }
-    fn visit_return_call_indirect(&mut self, index: u32, table_index: u32) -> Self::Output {
-        self.check_call_indirect(index, table_index)?;
+    fn visit_return_call_indirect(&mut self, type_index: u32, table_index: u32) -> Self::Output {
+        self.check_call_indirect(type_index, table_index)?;
         self.check_return()?;
+        self.check_func_type_index_same_results(type_index)?;
         Ok(())
     }
     fn visit_drop(&mut self) -> Self::Output {
@@ -1877,18 +1917,12 @@ where
         self.pop_operand(Some(ty))?;
         Ok(())
     }
-    fn visit_memory_size(&mut self, mem: u32, mem_byte: u8) -> Self::Output {
-        if mem_byte != 0 && !self.features.multi_memory() {
-            bail!(self.offset, "multi-memory not enabled: zero byte expected");
-        }
+    fn visit_memory_size(&mut self, mem: u32) -> Self::Output {
         let index_ty = self.check_memory_index(mem)?;
         self.push_operand(index_ty)?;
         Ok(())
     }
-    fn visit_memory_grow(&mut self, mem: u32, mem_byte: u8) -> Self::Output {
-        if mem_byte != 0 && !self.features.multi_memory() {
-            bail!(self.offset, "multi-memory not enabled: zero byte expected");
-        }
+    fn visit_memory_grow(&mut self, mem: u32) -> Self::Output {
         let index_ty = self.check_memory_index(mem)?;
         self.pop_operand(Some(index_ty))?;
         self.push_operand(index_ty)?;
diff --git a/crates/wasmprinter/src/operator.rs b/crates/wasmprinter/src/operator.rs
index 6b6f6211..6912582a 100644
--- a/crates/wasmprinter/src/operator.rs
+++ b/crates/wasmprinter/src/operator.rs
@@ -421,13 +421,12 @@ macro_rules! define_visit {
     // when an index is 0 or similar. The final case in this list is the
     // catch-all which prints each payload individually based on the name of the
     // payload field.
-    (payload $self:ident CallIndirect $ty:ident $table:ident $byte:ident) => (
+    (payload $self:ident CallIndirect $ty:ident $table:ident) => (
         if $table != 0 {
             $self.push_str(" ");
             $self.table_index($table)?;
         }
         $self.type_index($ty)?;
-        let _ = $byte;
     );
     (payload $self:ident ReturnCallIndirect $ty:ident $table:ident) => (
         if $table != 0 {
@@ -469,7 +468,13 @@ macro_rules! define_visit {
             $self.table_index($src)?;
         }
     );
-    (payload $self:ident $mem_op:ident $mem:ident mem_byte) => (
+    (payload $self:ident MemoryGrow $mem:ident) => (
+        if $mem != 0 {
+            $self.push_str(" ");
+            $self.memory_index($mem)?;
+        }
+    );
+    (payload $self:ident MemorySize $mem:ident) => (
         if $mem != 0 {
             $self.push_str(" ");
             $self.memory_index($mem)?;
diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml
index af878c4b..485c20b8 100644
--- a/crates/wast/Cargo.toml
+++ b/crates/wast/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "wast"
-version = "209.0.0"
+version = "209.0.1"
 authors = ["Alex Crichton <alex@alexcrichton.com>"]
 edition.workspace = true
 license = "Apache-2.0 WITH LLVM-exception"
diff --git a/crates/wat/Cargo.toml b/crates/wat/Cargo.toml
index 87fed3c3..ade0e261 100644
--- a/crates/wat/Cargo.toml
+++ b/crates/wat/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "wat"
-version = "1.209.0"
+version = "1.209.1"
 authors = ["Alex Crichton <alex@alexcrichton.com>"]
 edition.workspace = true
 license = "Apache-2.0 WITH LLVM-exception"
diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs
index eefd2bcd..6f0a5b61 100644
--- a/crates/wit-component/src/gc.rs
+++ b/crates/wit-component/src/gc.rs
@@ -1033,8 +1033,6 @@ macro_rules! define_visit {
     (mark_live $self:ident $arg:ident lanes) => {};
     (mark_live $self:ident $arg:ident flags) => {};
     (mark_live $self:ident $arg:ident value) => {};
-    (mark_live $self:ident $arg:ident mem_byte) => {};
-    (mark_live $self:ident $arg:ident table_byte) => {};
     (mark_live $self:ident $arg:ident local_index) => {};
     (mark_live $self:ident $arg:ident relative_depth) => {};
     (mark_live $self:ident $arg:ident tag_index) => {};
@@ -1186,21 +1184,12 @@ macro_rules! define_encode {
     (mk BrTable $arg:ident) => ({
         BrTable($arg.0, $arg.1)
     });
-    (mk CallIndirect $ty:ident $table:ident $table_byte:ident) => ({
-        let _ = $table_byte;
+    (mk CallIndirect $ty:ident $table:ident) => ({
         CallIndirect { ty: $ty, table: $table }
     });
     (mk ReturnCallIndirect $ty:ident $table:ident) => (
         ReturnCallIndirect { ty: $ty, table: $table }
     );
-    (mk MemorySize $mem:ident $mem_byte:ident) => ({
-        let _ = $mem_byte;
-        MemorySize($mem)
-    });
-    (mk MemoryGrow $mem:ident $mem_byte:ident) => ({
-        let _ = $mem_byte;
-        MemoryGrow($mem)
-    });
     (mk TryTable $try_table:ident) => ({
         let _ = $try_table;
         unimplemented_try_table()


Test patch:
diff --git a/tests/cli/dump-llvm-object.wat.stdout b/tests/cli/dump-llvm-object.wat.stdout
index fbf6d9cf..6d48409d 100644
--- a/tests/cli/dump-llvm-object.wat.stdout
+++ b/tests/cli/dump-llvm-object.wat.stdout
@@ -102,7 +102,7 @@
   0xf2 | 20 03       | local_get local_index:3
   0xf4 | 24 00       | global_set global_index:0
   0xf6 | 20 00       | local_get local_index:0
-  0xf8 | 11 05 00    | call_indirect type_index:5 table_index:0 table_byte:0
+  0xf8 | 11 05 00    | call_indirect type_index:5 table_index:0
   0xfb | 41 10       | i32_const value:16
   0xfd | 21 04       | local_set local_index:4
   0xff | 20 03       | local_get local_index:3
diff --git a/tests/local/function-references/return-call.wast b/tests/local/function-references/return-call.wast
new file mode 100644
index 00000000..c1c56079
--- /dev/null
+++ b/tests/local/function-references/return-call.wast
@@ -0,0 +1,37 @@
+;; Test that various return calls must exactly match the callee's returns, not
+;; simply leave the operand stack in a state where a `call; return` would
+;; otherwise be valid, but with some dangling stack values. Those dangling stack
+;; values are valid for regular calls, but not for return calls.
+
+(assert_invalid
+  (module
+    (func $f (result i32 i32) unreachable)
+    (func (result i32)
+      return_call $f
+    )
+  )
+  "type mismatch: current function requires result type [i32] but callee returns [i32 i32]"
+)
+
+(assert_invalid
+  (module
+    (type $ty (func (result i32 i32)))
+    (import "env" "table" (table $table 0 funcref))
+    (func (param i32) (result i32)
+      local.get 0
+      return_call_indirect $table (type $ty)
+    )
+  )
+  "type mismatch: current function requires result type [i32] but callee returns [i32 i32]"
+)
+
+(assert_invalid
+  (module
+    (type $ty (func (result i32 i32)))
+    (func (param funcref) (result i32)
+      local.get 0
+      return_call_ref $ty
+    )
+  )
+  "type mismatch: current function requires result type [i32] but callee returns [i32 i32]"
+)
diff --git a/tests/snapshots/local/function-references/return-call.wast.json b/tests/snapshots/local/function-references/return-call.wast.json
new file mode 100644
index 00000000..7ea31ec6
--- /dev/null
+++ b/tests/snapshots/local/function-references/return-call.wast.json
@@ -0,0 +1,26 @@
+{
+  "source_filename": "tests/local/function-references/return-call.wast",
+  "commands": [
+    {
+      "type": "assert_invalid",
+      "line": 7,
+      "filename": "return-call.0.wasm",
+      "text": "type mismatch: current function requires result type [i32] but callee returns [i32 i32]",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 17,
+      "filename": "return-call.1.wasm",
+      "text": "type mismatch: current function requires result type [i32] but callee returns [i32 i32]",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 29,
+      "filename": "return-call.2.wasm",
+      "text": "type mismatch: current function requires result type [i32] but callee returns [i32 i32]",
+      "module_type": "binary"
+    }
+  ]
+}
\ No newline at end of file