diff --git a/crates/wasm-encoder/src/core/code.rs b/crates/wasm-encoder/src/core/code.rs index 8fd62c0f72..8f0f3c3b28 100644 --- a/crates/wasm-encoder/src/core/code.rs +++ b/crates/wasm-encoder/src/core/code.rs @@ -367,6 +367,7 @@ pub enum Instruction<'a> { DataDrop(u32), MemoryCopy { src_mem: u32, dst_mem: u32 }, MemoryFill(u32), + MemoryDiscard(u32), // Numeric instructions. I32Const(i32), @@ -1092,6 +1093,11 @@ impl Encode for Instruction<'_> { sink.push(0x0b); mem.encode(sink); } + Instruction::MemoryDiscard(mem) => { + sink.push(0xfc); + sink.push(0x12); + mem.encode(sink); + } // Numeric instructions. Instruction::I32Const(x) => { diff --git a/crates/wasm-shrink/src/lib.rs b/crates/wasm-shrink/src/lib.rs index a64a3bea73..d37667592a 100755 --- a/crates/wasm-shrink/src/lib.rs +++ b/crates/wasm-shrink/src/lib.rs @@ -234,6 +234,7 @@ impl ShrinkRun { sign_extension: true, component_model: false, floats: true, + memory_control: true, }); validator.validate_all(wasm)?; diff --git a/crates/wasm-smith/tests/core.rs b/crates/wasm-smith/tests/core.rs index d8236db8b3..367b8006ad 100644 --- a/crates/wasm-smith/tests/core.rs +++ b/crates/wasm-smith/tests/core.rs @@ -299,6 +299,7 @@ fn parser_features_from_config(config: &impl Config) -> WasmFeatures { floats: true, extended_const: false, component_model: false, + memory_control: false, } } diff --git a/crates/wasmparser/benches/benchmark.rs b/crates/wasmparser/benches/benchmark.rs index 9d0019b3e3..dcfc4c38bc 100644 --- a/crates/wasmparser/benches/benchmark.rs +++ b/crates/wasmparser/benches/benchmark.rs @@ -251,6 +251,7 @@ fn define_benchmarks(c: &mut Criterion) { mutable_global: true, saturating_float_to_int: true, sign_extension: true, + memory_control: true, }) } diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs index 888d14331c..e8651e31ae 100644 --- a/crates/wasmparser/src/binary_reader.rs +++ b/crates/wasmparser/src/binary_reader.rs @@ -1046,6 +1046,11 @@ impl<'a> BinaryReader<'a> { visitor.visit_table_fill(table) } + 0x12 => { + let mem = self.read_var_u32()?; + visitor.visit_memory_discard(mem) + } + _ => bail!(pos, "unknown 0xfc subopcode: 0x{code:x}"), }) } diff --git a/crates/wasmparser/src/lib.rs b/crates/wasmparser/src/lib.rs index 7505f8b3b0..bc173267c8 100644 --- a/crates/wasmparser/src/lib.rs +++ b/crates/wasmparser/src/lib.rs @@ -338,6 +338,11 @@ macro_rules! for_each_operator { @reference_types TableGrow { table: u32 } => visit_table_grow @reference_types TableSize { table: u32 } => visit_table_size + // OxFC prefixed operators + // memory control (experimental) + // https://github.com/WebAssembly/design/issues/1439 + @memory_control MemoryDiscard { mem: u32 } => visit_memory_discard + // 0xFE prefixed operators // threads // https://github.com/WebAssembly/threads diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index cb4aca61f2..4f5d0bfa26 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -241,6 +241,8 @@ pub struct WasmFeatures { pub extended_const: bool, /// The WebAssembly component model proposal. pub component_model: bool, + /// The WebAssembly memory control proposal + pub memory_control: bool, } impl WasmFeatures { @@ -277,6 +279,7 @@ impl Default for WasmFeatures { memory64: false, extended_const: false, component_model: false, + memory_control: false, // on-by-default features mutable_global: true, diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs index 6e32b18455..c43e6e7987 100644 --- a/crates/wasmparser/src/validator/operators.rs +++ b/crates/wasmparser/src/validator/operators.rs @@ -936,6 +936,7 @@ macro_rules! validate_proposal { (desc sign_extension) => ("sign extension operations"); (desc exceptions) => ("exceptions"); (desc tail_call) => ("tail calls"); + (desc memory_control) => ("memory control"); } impl<'a, T> VisitOperator<'a> for WasmProposalValidator<'_, '_, T> @@ -3018,6 +3019,12 @@ where self.pop_operand(Some(ty))?; Ok(()) } + fn visit_memory_discard(&mut self, mem: u32) -> Self::Output { + let ty = self.check_memory_index(mem)?; + self.pop_operand(Some(ty))?; + self.pop_operand(Some(ty))?; + Ok(()) + } fn visit_table_init(&mut self, segment: u32, table: u32) -> Self::Output { if table > 0 {} let table = match self.resources.table_at(table) { diff --git a/crates/wasmprinter/src/operator.rs b/crates/wasmprinter/src/operator.rs index 4a3558b9b2..b651b956d7 100644 --- a/crates/wasmprinter/src/operator.rs +++ b/crates/wasmprinter/src/operator.rs @@ -279,6 +279,10 @@ macro_rules! define_visit { $self.memory_index($mem)?; } ); + (payload $self:ident MemoryDiscard $mem:ident) => ( + $self.push_str(" "); + $self.memory_index($mem)?; + ); (payload $self:ident I32Const $val:ident) => (write!($self.result(), " {}", $val)?); (payload $self:ident I64Const $val:ident) => (write!($self.result(), " {}", $val)?); (payload $self:ident F32Const $val:ident) => ( @@ -362,6 +366,7 @@ macro_rules! define_visit { (name MemoryInit) => ("memory.init"); (name MemoryCopy) => ("memory.copy"); (name MemoryFill) => ("memory.fill"); + (name MemoryDiscard) => ("memory.discard"); (name DataDrop) => ("data.drop"); (name ElemDrop) => ("elem.drop"); (name TableInit) => ("table.init"); diff --git a/crates/wast/src/core/expr.rs b/crates/wast/src/core/expr.rs index 0899d97069..b7be9f0553 100644 --- a/crates/wast/src/core/expr.rs +++ b/crates/wast/src/core/expr.rs @@ -568,6 +568,7 @@ instructions! { MemoryInit(MemoryInit<'a>) : [0xfc, 0x08] : "memory.init", MemoryCopy(MemoryCopy<'a>) : [0xfc, 0x0a] : "memory.copy", MemoryFill(MemoryArg<'a>) : [0xfc, 0x0b] : "memory.fill", + MemoryDiscard(MemoryArg<'a>) : [0xfc, 0x12] : "memory.discard", DataDrop(Index<'a>) : [0xfc, 0x09] : "data.drop", ElemDrop(Index<'a>) : [0xfc, 0x0d] : "elem.drop", TableInit(TableInit<'a>) : [0xfc, 0x0c] : "table.init", diff --git a/fuzz/fuzz_targets/validate.rs b/fuzz/fuzz_targets/validate.rs index 9fb059ee83..b1293db5de 100644 --- a/fuzz/fuzz_targets/validate.rs +++ b/fuzz/fuzz_targets/validate.rs @@ -36,8 +36,9 @@ fuzz_target!(|data: &[u8]| { mutable_global: (byte2 & 0b0010_0000) != 0, saturating_float_to_int: (byte2 & 0b0100_0000) != 0, sign_extension: (byte2 & 0b1000_0000) != 0, + memory_control: (byte3 & 0b0000_0001) != 0, }); - let use_maybe_invalid = byte3 & 0b0000_0001 != 0; + let use_maybe_invalid = byte3 & 0b0000_0010 != 0; let wasm = &data[3..]; if log::log_enabled!(log::Level::Debug) { diff --git a/tests/local/memory-discard.wat b/tests/local/memory-discard.wat new file mode 100644 index 0000000000..2059aad4dc --- /dev/null +++ b/tests/local/memory-discard.wat @@ -0,0 +1,7 @@ +(module + (memory i32 2) + + (func + (memory.discard (i32.const 0) (i32.const 65536)) + ) +) \ No newline at end of file diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index 99f87c10bc..569c92abe6 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -537,6 +537,7 @@ impl TestState { saturating_float_to_int: true, sign_extension: true, mutable_global: true, + memory_control: true, }; for part in test.iter().filter_map(|t| t.to_str()) { match part { diff --git a/tests/snapshots/local/memory-discard.wat.print b/tests/snapshots/local/memory-discard.wat.print new file mode 100644 index 0000000000..0dff92fcbf --- /dev/null +++ b/tests/snapshots/local/memory-discard.wat.print @@ -0,0 +1,9 @@ +(module + (type (;0;) (func)) + (func (;0;) (type 0) + i32.const 0 + i32.const 65536 + memory.discard 0 + ) + (memory (;0;) 2) +) \ No newline at end of file