diff --git a/crates/wasm-encoder/src/core/code.rs b/crates/wasm-encoder/src/core/code.rs index d493b33aa4..4ff071b166 100644 --- a/crates/wasm-encoder/src/core/code.rs +++ b/crates/wasm-encoder/src/core/code.rs @@ -1223,6 +1223,12 @@ pub enum Instruction<'a> { cont_type_index: u32, tag_index: u32, }, + + // Wide Arithmetic + I64Add128, + I64Sub128, + I64MulWideS, + I64MulWideU, } impl Encode for Instruction<'_> { @@ -3757,6 +3763,22 @@ impl Encode for Instruction<'_> { cont_type_index.encode(sink); tag_index.encode(sink); } + Instruction::I64Add128 => { + sink.push(0xFC); + 19u32.encode(sink); + } + Instruction::I64Sub128 => { + sink.push(0xFC); + 20u32.encode(sink); + } + Instruction::I64MulWideS => { + sink.push(0xFC); + 21u32.encode(sink); + } + Instruction::I64MulWideU => { + sink.push(0xFC); + 22u32.encode(sink); + } } } } diff --git a/crates/wasm-smith/src/config.rs b/crates/wasm-smith/src/config.rs index b6ced3ffb1..fda509936c 100644 --- a/crates/wasm-smith/src/config.rs +++ b/crates/wasm-smith/src/config.rs @@ -599,6 +599,13 @@ define_config! { /// /// Defaults to `false`. pub allow_invalid_funcs: bool = false, + + /// Determines whether the [wide-arithmetic proposal] is enabled. + /// + /// [wide-arithmetic proposal]: https://github.com/WebAssembly/wide-arithmetic + /// + /// Defaults to `false`. + pub wide_arithmetic_enabled: bool = false, } } @@ -738,6 +745,7 @@ impl<'a> Arbitrary<'a> for Config { // Proposals that are not stage4+ are disabled by default. memory64_enabled: false, custom_page_sizes_enabled: false, + wide_arithmetic_enabled: false, }; config.sanitize(); Ok(config) diff --git a/crates/wasm-smith/src/core/code_builder.rs b/crates/wasm-smith/src/core/code_builder.rs index 8f63a196b2..310800bf8a 100644 --- a/crates/wasm-smith/src/core/code_builder.rs +++ b/crates/wasm-smith/src/core/code_builder.rs @@ -590,6 +590,10 @@ instructions! { (Some(simd_v128_v128_on_stack_relaxed), i16x8_relaxed_q15mulr_s, VectorInt), (Some(simd_v128_v128_on_stack_relaxed), i16x8_relaxed_dot_i8x16_i7x16_s, VectorInt), (Some(simd_v128_v128_v128_on_stack_relaxed), i32x4_relaxed_dot_i8x16_i7x16_add_s, VectorInt), + (Some(wide_arithmetic_binop128_on_stack), i64_add128, NumericInt), + (Some(wide_arithmetic_binop128_on_stack), i64_sub128, NumericInt), + (Some(wide_arithmetic_mul_wide_on_stack), i64_mul_wide_s, NumericInt), + (Some(wide_arithmetic_mul_wide_on_stack), i64_mul_wide_u, NumericInt), } pub(crate) struct CodeBuilderAllocations { @@ -7266,3 +7270,61 @@ simd_ternop!( I32x4RelaxedDotI8x16I7x16AddS, i32x4_relaxed_dot_i8x16_i7x16_add_s ); + +#[inline] +fn wide_arithmetic_binop128_on_stack(module: &Module, builder: &mut CodeBuilder) -> bool { + module.config.wide_arithmetic_enabled && builder.types_on_stack(module, &[ValType::I64; 4]) +} + +#[inline] +fn wide_arithmetic_mul_wide_on_stack(module: &Module, builder: &mut CodeBuilder) -> bool { + module.config.wide_arithmetic_enabled && builder.types_on_stack(module, &[ValType::I64; 2]) +} + +fn i64_add128( + _: &mut Unstructured, + module: &Module, + builder: &mut CodeBuilder, + instructions: &mut Vec, +) -> Result<()> { + builder.pop_operands(module, &[ValType::I64; 4]); + builder.push_operands(&[ValType::I64; 2]); + instructions.push(Instruction::I64Add128); + Ok(()) +} + +fn i64_sub128( + _: &mut Unstructured, + module: &Module, + builder: &mut CodeBuilder, + instructions: &mut Vec, +) -> Result<()> { + builder.pop_operands(module, &[ValType::I64; 4]); + builder.push_operands(&[ValType::I64; 2]); + instructions.push(Instruction::I64Sub128); + Ok(()) +} + +fn i64_mul_wide_s( + _: &mut Unstructured, + module: &Module, + builder: &mut CodeBuilder, + instructions: &mut Vec, +) -> Result<()> { + builder.pop_operands(module, &[ValType::I64; 2]); + builder.push_operands(&[ValType::I64; 2]); + instructions.push(Instruction::I64MulWideS); + Ok(()) +} + +fn i64_mul_wide_u( + _: &mut Unstructured, + module: &Module, + builder: &mut CodeBuilder, + instructions: &mut Vec, +) -> Result<()> { + builder.pop_operands(module, &[ValType::I64; 2]); + builder.push_operands(&[ValType::I64; 2]); + instructions.push(Instruction::I64MulWideU); + Ok(()) +} diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs index f21748fa6d..aab5fc33b2 100644 --- a/crates/wasmparser/src/binary_reader.rs +++ b/crates/wasmparser/src/binary_reader.rs @@ -1357,6 +1357,11 @@ impl<'a> BinaryReader<'a> { visitor.visit_memory_discard(mem) } + 0x13 => visitor.visit_i64_add128(), + 0x14 => visitor.visit_i64_sub128(), + 0x15 => visitor.visit_i64_mul_wide_s(), + 0x16 => visitor.visit_i64_mul_wide_u(), + _ => bail!(pos, "unknown 0xfc subopcode: 0x{code:x}"), }) } diff --git a/crates/wasmparser/src/features.rs b/crates/wasmparser/src/features.rs index e595111fcd..35fb08cb10 100644 --- a/crates/wasmparser/src/features.rs +++ b/crates/wasmparser/src/features.rs @@ -227,6 +227,8 @@ define_wasm_features! { pub gc_types: GC_TYPES(1 << 26) = true; /// The WebAssembly [stack-switching proposal](https://github.com/WebAssembly/stack-switching). pub stack_switching: STACK_SWITCHING(1 << 27) = false; + /// The WebAssembly [wide-arithmetic proposal](https://github.com/WebAssembly/wide-arithmetic). + pub wide_arithmetic: WIDE_ARITHMETIC(1 << 28) = false; } } diff --git a/crates/wasmparser/src/lib.rs b/crates/wasmparser/src/lib.rs index fa5bac1df4..5bebb5f43f 100644 --- a/crates/wasmparser/src/lib.rs +++ b/crates/wasmparser/src/lib.rs @@ -815,6 +815,11 @@ macro_rules! for_each_operator { @stack_switching Resume { cont_type_index: u32, resume_table: $crate::ResumeTable } => visit_resume (arity 1 type -> type) @stack_switching ResumeThrow { cont_type_index: u32, tag_index: u32, resume_table: $crate::ResumeTable } => visit_resume_throw (arity 1 tag -> type) @stack_switching Switch { cont_type_index: u32, tag_index: u32 } => visit_switch (arity type -> ~switch) + + @wide_arithmetic I64Add128 => visit_i64_add128 (arity 4 -> 2) + @wide_arithmetic I64Sub128 => visit_i64_sub128 (arity 4 -> 2) + @wide_arithmetic I64MulWideS => visit_i64_mul_wide_s (arity 2 -> 2) + @wide_arithmetic I64MulWideU => visit_i64_mul_wide_u (arity 2 -> 2) } }; } diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs index 98ef2ca47e..29571ceb7c 100644 --- a/crates/wasmparser/src/validator/operators.rs +++ b/crates/wasmparser/src/validator/operators.rs @@ -1640,6 +1640,24 @@ where .zip(ts2.iter()) .all(|(ty1, ty2)| self.resources.is_subtype(*ty1, *ty2)) } + + fn check_binop128(&mut self) -> Result<()> { + self.pop_operand(Some(ValType::I64))?; + self.pop_operand(Some(ValType::I64))?; + self.pop_operand(Some(ValType::I64))?; + self.pop_operand(Some(ValType::I64))?; + self.push_operand(ValType::I64)?; + self.push_operand(ValType::I64)?; + Ok(()) + } + + fn check_i64_mul_wide(&mut self) -> Result<()> { + self.pop_operand(Some(ValType::I64))?; + self.pop_operand(Some(ValType::I64))?; + self.push_operand(ValType::I64)?; + self.push_operand(ValType::I64)?; + Ok(()) + } } pub fn ty_to_str(ty: ValType) -> &'static str { @@ -1704,6 +1722,7 @@ macro_rules! validate_proposal { (desc gc) => ("gc"); (desc legacy_exceptions) => ("legacy exceptions"); (desc stack_switching) => ("stack switching"); + (desc wide_arithmetic) => ("wide arithmetic"); } impl<'a, T> VisitOperator<'a> for WasmProposalValidator<'_, '_, T> @@ -5035,6 +5054,18 @@ where } Ok(()) } + fn visit_i64_add128(&mut self) -> Result<()> { + self.check_binop128() + } + fn visit_i64_sub128(&mut self) -> Result<()> { + self.check_binop128() + } + fn visit_i64_mul_wide_s(&mut self) -> Result<()> { + self.check_i64_mul_wide() + } + fn visit_i64_mul_wide_u(&mut self) -> Result<()> { + self.check_i64_mul_wide() + } } #[derive(Clone, Debug)] diff --git a/crates/wasmprinter/src/operator.rs b/crates/wasmprinter/src/operator.rs index 689c469b50..6456ecc3d9 100644 --- a/crates/wasmprinter/src/operator.rs +++ b/crates/wasmprinter/src/operator.rs @@ -1379,7 +1379,11 @@ macro_rules! define_visit { (name Suspend) => ("suspend"); (name Resume) => ("resume"); (name ResumeThrow) => ("resume_throw"); - (name Switch) => ("switch") + (name Switch) => ("switch"); + (name I64Add128) => ("i64.add128"); + (name I64Sub128) => ("i64.sub128"); + (name I64MulWideS) => ("i64.mul_wide_s"); + (name I64MulWideU) => ("i64.mul_wide_u"); } impl<'a> VisitOperator<'a> for PrintOperator<'_, '_, '_, '_> { diff --git a/crates/wast/src/core/expr.rs b/crates/wast/src/core/expr.rs index 21bd6d53ae..228f46b914 100644 --- a/crates/wast/src/core/expr.rs +++ b/crates/wast/src/core/expr.rs @@ -1195,6 +1195,12 @@ instructions! { Resume(Resume<'a>) : [0xe3] : "resume", ResumeThrow(ResumeThrow<'a>) : [0xe4] : "resume_throw", Switch(Switch<'a>) : [0xe5] : "switch", + + // Wide arithmetic proposal + I64Add128 : [0xfc, 19] : "i64.add128", + I64Sub128 : [0xfc, 20] : "i64.sub128", + I64MulWideS : [0xfc, 21] : "i64.mul_wide_s", + I64MulWideU : [0xfc, 22] : "i64.mul_wide_u", } } diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs index 8244bb9c18..9130954a5d 100644 --- a/fuzz/src/lib.rs +++ b/fuzz/src/lib.rs @@ -60,6 +60,7 @@ pub fn generate_valid_component( config.memory64_enabled = u.arbitrary()?; config.exceptions_enabled = u.arbitrary()?; config.canonicalize_nans = u.arbitrary()?; + config.wide_arithmetic_enabled = u.arbitrary()?; configure(&mut config, u)?; @@ -111,6 +112,10 @@ pub fn validator_for_config(config: &Config) -> wasmparser::Validator { // this point. features.set(WasmFeatures::FUNCTION_REFERENCES, config.gc_enabled); features.set(WasmFeatures::GC, config.gc_enabled); + features.set( + WasmFeatures::WIDE_ARITHMETIC, + config.wide_arithmetic_enabled, + ); wasmparser::Validator::new_with_features(features) } diff --git a/tests/cli/validate-unknown-features.wat.stderr b/tests/cli/validate-unknown-features.wat.stderr index 3156c21bf6..d0d274b716 100644 --- a/tests/cli/validate-unknown-features.wat.stderr +++ b/tests/cli/validate-unknown-features.wat.stderr @@ -1,4 +1,4 @@ error: invalid value 'unknown' for '--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, component-model-multiple-returns, legacy-exceptions, gc-types, stack-switching, mvp, wasm1, wasm2, wasm3, all +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, component-model-multiple-returns, legacy-exceptions, gc-types, stack-switching, wide-arithmetic, mvp, wasm1, wasm2, wasm3, all For more information, try '--help'. diff --git a/tests/local/missing-features/wide-arithmetic.wast b/tests/local/missing-features/wide-arithmetic.wast new file mode 100644 index 0000000000..4bf70209b3 --- /dev/null +++ b/tests/local/missing-features/wide-arithmetic.wast @@ -0,0 +1,47 @@ +(assert_invalid + (module + (func (param i64 i64 i64 i64) + local.get 0 + local.get 1 + local.get 2 + local.get 3 + i64.add128 + drop + drop) + ) + "wide arithmetic support is not enabled") + +(assert_invalid + (module + (func (param i64 i64 i64 i64) + local.get 0 + local.get 1 + local.get 2 + local.get 3 + i64.sub128 + drop + drop) + ) + "wide arithmetic support is not enabled") + +(assert_invalid + (module + (func (param i64 i64) + local.get 0 + local.get 1 + i64.mul_wide_s + drop + drop) + ) + "wide arithmetic support is not enabled") + +(assert_invalid + (module + (func (param i64 i64) + local.get 0 + local.get 1 + i64.mul_wide_u + drop + drop) + ) + "wide arithmetic support is not enabled") diff --git a/tests/local/wide-arithmetic.wast b/tests/local/wide-arithmetic.wast new file mode 100644 index 0000000000..83970d90d7 --- /dev/null +++ b/tests/local/wide-arithmetic.wast @@ -0,0 +1,94 @@ +(module + (func (param i64 i64 i64 i64) (result i64 i64) + local.get 0 + local.get 1 + local.get 2 + local.get 3 + i64.add128) + (func (param i64 i64 i64 i64) (result i64 i64) + local.get 0 + local.get 1 + local.get 2 + local.get 3 + i64.sub128) + (func (param i64 i64) (result i64 i64) + local.get 0 + local.get 1 + i64.mul_wide_s) + (func (param i64 i64) (result i64 i64) + local.get 0 + local.get 1 + i64.mul_wide_u) +) + +(assert_invalid + (module + (func (param i64 i64 i64 i64) (result i64) + local.get 0 + local.get 1 + local.get 2 + local.get 3 + i64.add128) + ) + "type mismatch") +(assert_invalid + (module + (func (param i64 i64 i64) (result i64 i64) + local.get 0 + local.get 1 + local.get 2 + i64.add128) + ) + "type mismatch") + +(assert_invalid + (module + (func (param i64 i64 i64 i64) (result i64) + local.get 0 + local.get 1 + local.get 2 + local.get 3 + i64.sub128) + ) + "type mismatch") +(assert_invalid + (module + (func (param i64 i64 i64) (result i64 i64) + local.get 0 + local.get 1 + local.get 2 + i64.sub128) + ) + "type mismatch") + +(assert_invalid + (module + (func (param i64 i64) (result i64) + local.get 0 + local.get 1 + i64.mul_wide_s) + ) + "type mismatch") +(assert_invalid + (module + (func (param i64) (result i64 i64) + local.get 0 + i64.mul_wide_s) + ) + "type mismatch") + +(assert_invalid + (module + (func (param i64 i64) (result i64) + local.get 0 + local.get 1 + i64.mul_wide_u) + ) + "type mismatch") +(assert_invalid + (module + (func (param i64) (result i64 i64) + local.get 0 + i64.mul_wide_u) + ) + "type mismatch") diff --git a/tests/snapshots/local/missing-features/wide-arithmetic.wast.json b/tests/snapshots/local/missing-features/wide-arithmetic.wast.json new file mode 100644 index 0000000000..959d76a619 --- /dev/null +++ b/tests/snapshots/local/missing-features/wide-arithmetic.wast.json @@ -0,0 +1,33 @@ +{ + "source_filename": "tests/local/missing-features/wide-arithmetic.wast", + "commands": [ + { + "type": "assert_invalid", + "line": 2, + "filename": "wide-arithmetic.0.wasm", + "module_type": "binary", + "text": "wide arithmetic support is not enabled" + }, + { + "type": "assert_invalid", + "line": 15, + "filename": "wide-arithmetic.1.wasm", + "module_type": "binary", + "text": "wide arithmetic support is not enabled" + }, + { + "type": "assert_invalid", + "line": 28, + "filename": "wide-arithmetic.2.wasm", + "module_type": "binary", + "text": "wide arithmetic support is not enabled" + }, + { + "type": "assert_invalid", + "line": 39, + "filename": "wide-arithmetic.3.wasm", + "module_type": "binary", + "text": "wide arithmetic support is not enabled" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/local/wide-arithmetic.wast.json b/tests/snapshots/local/wide-arithmetic.wast.json new file mode 100644 index 0000000000..6ecf7c1211 --- /dev/null +++ b/tests/snapshots/local/wide-arithmetic.wast.json @@ -0,0 +1,67 @@ +{ + "source_filename": "tests/local/wide-arithmetic.wast", + "commands": [ + { + "type": "module", + "line": 1, + "filename": "wide-arithmetic.0.wasm", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 25, + "filename": "wide-arithmetic.1.wasm", + "module_type": "binary", + "text": "type mismatch" + }, + { + "type": "assert_invalid", + "line": 35, + "filename": "wide-arithmetic.2.wasm", + "module_type": "binary", + "text": "type mismatch" + }, + { + "type": "assert_invalid", + "line": 45, + "filename": "wide-arithmetic.3.wasm", + "module_type": "binary", + "text": "type mismatch" + }, + { + "type": "assert_invalid", + "line": 55, + "filename": "wide-arithmetic.4.wasm", + "module_type": "binary", + "text": "type mismatch" + }, + { + "type": "assert_invalid", + "line": 65, + "filename": "wide-arithmetic.5.wasm", + "module_type": "binary", + "text": "type mismatch" + }, + { + "type": "assert_invalid", + "line": 73, + "filename": "wide-arithmetic.6.wasm", + "module_type": "binary", + "text": "type mismatch" + }, + { + "type": "assert_invalid", + "line": 81, + "filename": "wide-arithmetic.7.wasm", + "module_type": "binary", + "text": "type mismatch" + }, + { + "type": "assert_invalid", + "line": 89, + "filename": "wide-arithmetic.8.wasm", + "module_type": "binary", + "text": "type mismatch" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/local/wide-arithmetic.wast/0.print b/tests/snapshots/local/wide-arithmetic.wast/0.print new file mode 100644 index 0000000000..ade68d47c5 --- /dev/null +++ b/tests/snapshots/local/wide-arithmetic.wast/0.print @@ -0,0 +1,28 @@ +(module + (type (;0;) (func (param i64 i64 i64 i64) (result i64 i64))) + (type (;1;) (func (param i64 i64) (result i64 i64))) + (func (;0;) (type 0) (param i64 i64 i64 i64) (result i64 i64) + local.get 0 + local.get 1 + local.get 2 + local.get 3 + i64.add128 + ) + (func (;1;) (type 0) (param i64 i64 i64 i64) (result i64 i64) + local.get 0 + local.get 1 + local.get 2 + local.get 3 + i64.sub128 + ) + (func (;2;) (type 1) (param i64 i64) (result i64 i64) + local.get 0 + local.get 1 + i64.mul_wide_s + ) + (func (;3;) (type 1) (param i64 i64) (result i64 i64) + local.get 0 + local.get 1 + i64.mul_wide_u + ) +)