Repository: bytecodealliance/wasmtime
Pull request #9403 (2024-10-08 21:37:08)

Implement the wide-arithmetic proposal
This commit implements the [wide-arithmetic] proposal in Wasmtime. This is a pretty easy proposal to implement due to Cranelift already having support for all the various instructions. The features implemented here are:

* Cranelift support for the four new instructions.
* A new `Config::wasm_wide_arithmetic` option.
* A new `-Wwide-arithmetic` CLI flag.
* A new `wasmtime_config_wasm_wide_arithmetic_set` C API function.
* Support for fuzzing this proposal
  * Generation is implemented in `wasm-smith` .
  * While it's off-by-default in `wasm-smith` it's enabled-by-default here.
  * Differential execution is only possible against Wasmtime right now.
  * Single-instruction module support was added for these new instructions.
* Tests for some simple cases plus randomly-generated tests.
* Example disassemblies for new instructions on Cranelift architectures.

[wide-arithmetic]: https://github.com/WebAssembly/wide-arithmetic

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/crates/c-api/include/wasmtime/config.h b/crates/c-api/include/wasmtime/config.h
index 0cd970ccc..e7f8ecc91 100644
--- a/crates/c-api/include/wasmtime/config.h
+++ b/crates/c-api/include/wasmtime/config.h
@@ -240,6 +240,14 @@ WASMTIME_CONFIG_PROP(void, wasm_multi_memory, bool)
  */
 WASMTIME_CONFIG_PROP(void, wasm_memory64, bool)
 
+/**
+ * \brief Configures whether the WebAssembly wide-arithmetic proposal is
+ * enabled.
+ *
+ * This setting is `false` by default.
+ */
+WASMTIME_CONFIG_PROP(void, wasm_wide_arithmetic, bool)
+
 #ifdef WASMTIME_FEATURE_COMPILER
 
 /**
diff --git a/crates/c-api/src/config.rs b/crates/c-api/src/config.rs
index 4ce46768a..27035283e 100644
--- a/crates/c-api/src/config.rs
+++ b/crates/c-api/src/config.rs
@@ -460,3 +460,8 @@ pub unsafe extern "C" fn wasmtime_config_host_memory_creator_set(
 pub extern "C" fn wasmtime_config_memory_init_cow_set(c: &mut wasm_config_t, enable: bool) {
     c.config.memory_init_cow(enable);
 }
+
+#[no_mangle]
+pub extern "C" fn wasmtime_config_wasm_wide_arithmetic_set(c: &mut wasm_config_t, enable: bool) {
+    c.config.wasm_wide_arithmetic(enable);
+}
diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs
index 990601495..015a9b678 100644
--- a/crates/cli-flags/src/lib.rs
+++ b/crates/cli-flags/src/lib.rs
@@ -281,6 +281,8 @@ wasmtime_option_group! {
         pub gc: Option<bool>,
         /// Configure support for the custom-page-sizes proposal.
         pub custom_page_sizes: Option<bool>,
+        /// Configure support for the wide-arithmetic proposal.
+        pub wide_arithmetic: Option<bool>,
     }
 
     enum Wasm {
@@ -724,6 +726,9 @@ impl CommonOptions {
         if let Some(enable) = self.wasm.custom_page_sizes.or(all) {
             config.wasm_custom_page_sizes(enable);
         }
+        if let Some(enable) = self.wasm.wide_arithmetic.or(all) {
+            config.wasm_wide_arithmetic(enable);
+        }
 
         macro_rules! handle_conditionally_compiled {
             ($(($feature:tt, $field:tt, $method:tt))*) => ($(
diff --git a/crates/cranelift/src/translate/code_translator.rs b/crates/cranelift/src/translate/code_translator.rs
index b7272b2e2..bcf3cd4e9 100644
--- a/crates/cranelift/src/translate/code_translator.rs
+++ b/crates/cranelift/src/translate/code_translator.rs
@@ -2793,13 +2793,37 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
             ));
         }
 
-        Operator::I64Add128
-        | Operator::I64Sub128
-        | Operator::I64MulWideS
-        | Operator::I64MulWideU => {
-            return Err(wasm_unsupported!(
-                "wide-arithmetic operators are not yet implemented"
-            ));
+        Operator::I64MulWideS => {
+            let (arg1, arg2) = state.pop2();
+            let arg1 = builder.ins().sextend(I128, arg1);
+            let arg2 = builder.ins().sextend(I128, arg2);
+            let result = builder.ins().imul(arg1, arg2);
+            let (lo, hi) = builder.ins().isplit(result);
+            state.push2(lo, hi);
+        }
+        Operator::I64MulWideU => {
+            let (arg1, arg2) = state.pop2();
+            let arg1 = builder.ins().uextend(I128, arg1);
+            let arg2 = builder.ins().uextend(I128, arg2);
+            let result = builder.ins().imul(arg1, arg2);
+            let (lo, hi) = builder.ins().isplit(result);
+            state.push2(lo, hi);
+        }
+        Operator::I64Add128 => {
+            let (arg1, arg2, arg3, arg4) = state.pop4();
+            let arg1 = builder.ins().iconcat(arg1, arg2);
+            let arg2 = builder.ins().iconcat(arg3, arg4);
+            let result = builder.ins().iadd(arg1, arg2);
+            let (res1, res2) = builder.ins().isplit(result);
+            state.push2(res1, res2);
+        }
+        Operator::I64Sub128 => {
+            let (arg1, arg2, arg3, arg4) = state.pop4();
+            let arg1 = builder.ins().iconcat(arg1, arg2);
+            let arg2 = builder.ins().iconcat(arg3, arg4);
+            let result = builder.ins().isub(arg1, arg2);
+            let (res1, res2) = builder.ins().isplit(result);
+            state.push2(res1, res2);
         }
     };
     Ok(())
diff --git a/crates/cranelift/src/translate/state.rs b/crates/cranelift/src/translate/state.rs
index 5f67f9009..155369e5e 100644
--- a/crates/cranelift/src/translate/state.rs
+++ b/crates/cranelift/src/translate/state.rs
@@ -295,6 +295,12 @@ impl FuncTranslationState {
         self.stack.push(val);
     }
 
+    /// Push two values.
+    pub(crate) fn push2(&mut self, val1: Value, val2: Value) {
+        self.stack.push(val1);
+        self.stack.push(val2);
+    }
+
     /// Push multiple values.
     pub(crate) fn pushn(&mut self, vals: &[Value]) {
         self.stack.extend_from_slice(vals);
diff --git a/crates/fuzzing/src/generators/config.rs b/crates/fuzzing/src/generators/config.rs
index b1355a5b0..512912a13 100644
--- a/crates/fuzzing/src/generators/config.rs
+++ b/crates/fuzzing/src/generators/config.rs
@@ -170,6 +170,7 @@ impl Config {
             .wasm_threads(self.module_config.config.threads_enabled)
             .wasm_function_references(self.module_config.config.gc_enabled)
             .wasm_gc(self.module_config.config.gc_enabled)
+            .wasm_wide_arithmetic(self.module_config.config.wide_arithmetic_enabled)
             .native_unwind_info(cfg!(target_os = "windows") || self.wasmtime.native_unwind_info)
             .cranelift_nan_canonicalization(self.wasmtime.canonicalize_nans)
             .cranelift_opt_level(self.wasmtime.opt_level.to_wasmtime())
@@ -474,6 +475,10 @@ impl WasmtimeConfig {
         // Not fully implemented in Wasmtime and fuzzing.
         config.gc_enabled = false;
 
+        // Off-by-default in wasm-smith but implemented in wasmtime, so give the
+        // fuzzers a chance to run it.
+        config.wide_arithmetic_enabled = u.arbitrary()?;
+
         // Winch doesn't support the same set of wasm proposal as Cranelift at
         // this time, so if winch is selected be sure to disable wasm proposals
         // in `Config` to ensure that Winch can compile the module that
@@ -485,6 +490,7 @@ impl WasmtimeConfig {
             config.threads_enabled = false;
             config.tail_call_enabled = false;
             config.reference_types_enabled = false;
+            config.wide_arithmetic_enabled = false;
 
             // Winch requires host trap handlers to be enabled at this time.
             self.signals_based_traps = true;
diff --git a/crates/fuzzing/src/generators/single_inst_module.rs b/crates/fuzzing/src/generators/single_inst_module.rs
index dcad77ee1..6177d0342 100644
--- a/crates/fuzzing/src/generators/single_inst_module.rs
+++ b/crates/fuzzing/src/generators/single_inst_module.rs
@@ -163,36 +163,38 @@ impl<'a> SingleInstModule<'a> {
 // instructions compactly and allow for easier changes to the Rust code (e.g.,
 // `SingleInstModule`).
 
-macro_rules! valtype {
-    (i32) => {
+macro_rules! valtypes {
+    (@list ($($ty:tt),*)) => {&[$(valtypes!(@one $ty)),*]};
+    (@list $ty:tt) => {&[valtypes!(@one $ty)]};
+    (@one i32) => {
         ValType::I32
     };
-    (i64) => {
+    (@one i64) => {
         ValType::I64
     };
-    (f32) => {
+    (@one f32) => {
         ValType::F32
     };
-    (f64) => {
+    (@one f64) => {
         ValType::F64
     };
-    (v128) => {
+    (@one v128) => {
         ValType::V128
     };
 }
 
 macro_rules! inst {
-    ($inst:ident, ($($arguments_ty:tt),*) -> $result_ty:tt) => {
-        inst! { $inst, ($($arguments_ty),*) -> $result_ty, |_| true }
+    ($inst:ident, $arguments:tt -> $results:tt) => {
+        inst! { $inst, $arguments -> $results, |_| true }
     };
-    ($inst:ident, ($($arguments_ty:tt),*) -> $result_ty:tt, $feature:expr) => {
-        inst! { $inst, ($($arguments_ty),*) -> $result_ty, $feature, None }
+    ($inst:ident, $arguments:tt -> $results:tt, $feature:expr) => {
+        inst! { $inst, $arguments -> $results, $feature, None }
     };
-    ($inst:ident, ($($arguments_ty:tt),*) -> $result_ty:tt, $feature:expr, $nan:expr) => {
+    ($inst:ident, $arguments:tt -> $results:tt, $feature:expr, $nan:expr) => {
         SingleInstModule {
             instruction: Instruction::$inst,
-            parameters: &[$(valtype!($arguments_ty)),*],
-            results: &[valtype!($result_ty)],
+            parameters: valtypes!(@list $arguments),
+            results: valtypes!(@list $results),
             feature: $feature,
             canonicalize_nan: $nan,
         }
@@ -568,6 +570,11 @@ static INSTRUCTIONS: &[SingleInstModule] = &[
     inst!(F64x2ConvertLowI32x4U, (v128) -> v128, |c| c.config.simd_enabled),
     inst!(F32x4DemoteF64x2Zero, (v128) -> v128, |c| c.config.simd_enabled),
     inst!(F64x2PromoteLowF32x4, (v128) -> v128, |c| c.config.simd_enabled),
+    // wide arithmetic
+    inst!(I64Add128, (i64, i64, i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled),
+    inst!(I64Sub128, (i64, i64, i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled),
+    inst!(I64MulWideS, (i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled),
+    inst!(I64MulWideU, (i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled),
 ];
 
 #[cfg(test)]
diff --git a/crates/fuzzing/src/oracles/diff_spec.rs b/crates/fuzzing/src/oracles/diff_spec.rs
index df4b4f314..4a22c8cb9 100644
--- a/crates/fuzzing/src/oracles/diff_spec.rs
+++ b/crates/fuzzing/src/oracles/diff_spec.rs
@@ -25,6 +25,7 @@ impl SpecInterpreter {
         config.reference_types_enabled = false;
         config.tail_call_enabled = false;
         config.relaxed_simd_enabled = false;
+        config.wide_arithmetic_enabled = false;
 
         Self
     }
diff --git a/crates/fuzzing/src/oracles/diff_v8.rs b/crates/fuzzing/src/oracles/diff_v8.rs
index 609b388ec..c2697b906 100644
--- a/crates/fuzzing/src/oracles/diff_v8.rs
+++ b/crates/fuzzing/src/oracles/diff_v8.rs
@@ -32,6 +32,7 @@ impl V8Engine {
         config.min_memories = config.min_memories.min(1);
         config.max_memories = config.max_memories.min(1);
         config.memory64_enabled = false;
+        config.wide_arithmetic_enabled = false;
 
         Self {
             isolate: Rc::new(RefCell::new(v8::Isolate::new(Default::default()))),
diff --git a/crates/fuzzing/src/oracles/diff_wasmi.rs b/crates/fuzzing/src/oracles/diff_wasmi.rs
index 37143c77d..0c8a3d95e 100644
--- a/crates/fuzzing/src/oracles/diff_wasmi.rs
+++ b/crates/fuzzing/src/oracles/diff_wasmi.rs
@@ -20,6 +20,7 @@ impl WasmiEngine {
         config.threads_enabled = false;
         config.exceptions_enabled = false;
         config.gc_enabled = false;
+        config.wide_arithmetic_enabled = false;
         config.max_memories = config.max_memories.min(1);
         config.min_memories = config.min_memories.min(1);
 
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index 84d7e7367..88138bec2 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -818,6 +818,17 @@ impl Config {
         self
     }
 
+    /// Configures whether the [WebAssembly wide-arithmetic][proposal] will be
+    /// enabled for compilation.
+    ///
+    /// This feature is `false` by default.
+    ///
+    /// [proposal]: https://github.com/WebAssembly/wide-arithmetic
+    pub fn wasm_wide_arithmetic(&mut self, enable: bool) -> &mut Self {
+        self.wasm_feature(WasmFeatures::WIDE_ARITHMETIC, enable);
+        self
+    }
+
     /// Configures whether the [WebAssembly Garbage Collection
     /// proposal][proposal] will be enabled for compilation.
     ///
diff --git a/docs/stability-tiers.md b/docs/stability-tiers.md
index f19b4842a..7fedfa984 100644
--- a/docs/stability-tiers.md
+++ b/docs/stability-tiers.md
@@ -73,6 +73,7 @@ For explanations of what each tier means see below.
 | Target               | Support for `#![no_std]`   | Support beyond CI checks    |
 | WebAssembly Proposal | [`memory64`]               | Unstable wasm proposal      |
 | WebAssembly Proposal | [`function-references`]    | Unstable wasm proposal      |
+| WebAssembly Proposal | [`wide-arithmetic`]        | Unstable wasm proposal      |
 
 [`memory64`]: https://github.com/WebAssembly/memory64/blob/master/proposals/memory64/Overview.md
 [`multi-memory`]: https://github.com/WebAssembly/multi-memory/blob/master/proposals/multi-memory/Overview.md
@@ -80,6 +81,7 @@ For explanations of what each tier means see below.
 [`component-model`]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md
 [`relaxed-simd`]: https://github.com/WebAssembly/relaxed-simd/blob/main/proposals/relaxed-simd/Overview.md
 [`function-references`]: https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md
+[`wide-arithmetic`]: https://github.com/WebAssembly/wide-arithmetic/blob/main/proposals/wide-arithmetic/Overview.md
 
 #### Tier 3
 


Test patch:
diff --git a/tests/disas/aarch64-wide-arithmetic.wat b/tests/disas/aarch64-wide-arithmetic.wat
new file mode 100644
index 000000000..c4e115422
--- /dev/null
+++ b/tests/disas/aarch64-wide-arithmetic.wat
@@ -0,0 +1,91 @@
+;;! target = "aarch64"
+;;! test = "compile"
+;;! flags = "-Wwide-arithmetic"
+
+(module
+  (func $add128 (param i64 i64 i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    local.get 2
+    local.get 3
+    i64.add128)
+
+  (func $sub128 (param i64 i64 i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    local.get 2
+    local.get 3
+    i64.sub128)
+
+  (func $signed (param i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_s)
+
+  (func $unsigned (param i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_u)
+
+  (func $signed_only_high (param i64 i64) (result i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_s
+    local.set 0
+    drop
+    local.get 0)
+
+  (func $unsigned_only_high (param i64 i64) (result i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_u
+    local.set 0
+    drop
+    local.get 0)
+)
+
+;; wasm[0]::function[0]::add128:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       adds    x2, x4, x6
+;;       adc     x3, x5, x7
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
+;;
+;; wasm[0]::function[1]::sub128:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       subs    x2, x4, x6
+;;       sbc     x3, x5, x7
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
+;;
+;; wasm[0]::function[2]::signed:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mul     x2, x4, x5
+;;       smulh   x3, x4, x5
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
+;;
+;; wasm[0]::function[3]::unsigned:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mul     x2, x4, x5
+;;       umulh   x3, x4, x5
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
+;;
+;; wasm[0]::function[4]::signed_only_high:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       smulh   x2, x4, x5
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
+;;
+;; wasm[0]::function[5]::unsigned_only_high:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       umulh   x2, x4, x5
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/riscv64-wide-arithmetic.wat b/tests/disas/riscv64-wide-arithmetic.wat
new file mode 100644
index 000000000..dc81d5a1d
--- /dev/null
+++ b/tests/disas/riscv64-wide-arithmetic.wat
@@ -0,0 +1,119 @@
+;;! target = "riscv64"
+;;! test = "compile"
+;;! flags = "-Wwide-arithmetic"
+
+(module
+  (func $add128 (param i64 i64 i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    local.get 2
+    local.get 3
+    i64.add128)
+
+  (func $sub128 (param i64 i64 i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    local.get 2
+    local.get 3
+    i64.sub128)
+
+  (func $signed (param i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_s)
+
+  (func $unsigned (param i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_u)
+
+  (func $signed_only_high (param i64 i64) (result i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_s
+    local.set 0
+    drop
+    local.get 0)
+
+  (func $unsigned_only_high (param i64 i64) (result i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_u
+    local.set 0
+    drop
+    local.get 0)
+)
+
+;; wasm[0]::function[0]::add128:
+;;       addi    sp, sp, -0x10
+;;       sd      ra, 8(sp)
+;;       sd      s0, 0(sp)
+;;       mv      s0, sp
+;;       add     a0, a2, a4
+;;       sltu    a1, a0, a4
+;;       add     a3, a3, a5
+;;       add     a1, a3, a1
+;;       ld      ra, 8(sp)
+;;       ld      s0, 0(sp)
+;;       addi    sp, sp, 0x10
+;;       ret
+;;
+;; wasm[0]::function[1]::sub128:
+;;       addi    sp, sp, -0x10
+;;       sd      ra, 8(sp)
+;;       sd      s0, 0(sp)
+;;       mv      s0, sp
+;;       sub     a0, a2, a4
+;;       sltu    a1, a2, a0
+;;       sub     a3, a3, a5
+;;       sub     a1, a3, a1
+;;       ld      ra, 8(sp)
+;;       ld      s0, 0(sp)
+;;       addi    sp, sp, 0x10
+;;       ret
+;;
+;; wasm[0]::function[2]::signed:
+;;       addi    sp, sp, -0x10
+;;       sd      ra, 8(sp)
+;;       sd      s0, 0(sp)
+;;       mv      s0, sp
+;;       mul     a0, a2, a3
+;;       mulh    a1, a2, a3
+;;       ld      ra, 8(sp)
+;;       ld      s0, 0(sp)
+;;       addi    sp, sp, 0x10
+;;       ret
+;;
+;; wasm[0]::function[3]::unsigned:
+;;       addi    sp, sp, -0x10
+;;       sd      ra, 8(sp)
+;;       sd      s0, 0(sp)
+;;       mv      s0, sp
+;;       mul     a0, a2, a3
+;;       mulhu   a1, a2, a3
+;;       ld      ra, 8(sp)
+;;       ld      s0, 0(sp)
+;;       addi    sp, sp, 0x10
+;;       ret
+;;
+;; wasm[0]::function[4]::signed_only_high:
+;;       addi    sp, sp, -0x10
+;;       sd      ra, 8(sp)
+;;       sd      s0, 0(sp)
+;;       mv      s0, sp
+;;       mulh    a0, a2, a3
+;;       ld      ra, 8(sp)
+;;       ld      s0, 0(sp)
+;;       addi    sp, sp, 0x10
+;;       ret
+;;
+;; wasm[0]::function[5]::unsigned_only_high:
+;;       addi    sp, sp, -0x10
+;;       sd      ra, 8(sp)
+;;       sd      s0, 0(sp)
+;;       mv      s0, sp
+;;       mulhu   a0, a2, a3
+;;       ld      ra, 8(sp)
+;;       ld      s0, 0(sp)
+;;       addi    sp, sp, 0x10
+;;       ret
diff --git a/tests/disas/s390x-wide-arithmetic.wat b/tests/disas/s390x-wide-arithmetic.wat
new file mode 100644
index 000000000..dfaf6ed83
--- /dev/null
+++ b/tests/disas/s390x-wide-arithmetic.wat
@@ -0,0 +1,145 @@
+;;! target = "s390x"
+;;! test = "compile"
+;;! flags = "-Wwide-arithmetic"
+
+(module
+  (func $add128 (param i64 i64 i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    local.get 2
+    local.get 3
+    i64.add128)
+
+  (func $sub128 (param i64 i64 i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    local.get 2
+    local.get 3
+    i64.sub128)
+
+  (func $signed (param i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_s)
+
+  (func $unsigned (param i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_u)
+
+  (func $signed_only_high (param i64 i64) (result i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_s
+    local.set 0
+    drop
+    local.get 0)
+
+  (func $unsigned_only_high (param i64 i64) (result i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_u
+    local.set 0
+    drop
+    local.get 0)
+)
+
+;; wasm[0]::function[0]::add128:
+;;       lg      %r1, 8(%r2)
+;;       lg      %r1, 0(%r1)
+;;       la      %r1, 0xa0(%r1)
+;;       clgrtle %r15, %r1
+;;       stmg    %r14, %r15, 0x70(%r15)
+;;       lgr     %r1, %r15
+;;       aghi    %r15, -0xa0
+;;       stg     %r1, 0(%r15)
+;;       vlvgp   %v17, %r5, %r4
+;;       vlvgp   %v18, %r7, %r6
+;;       vaq     %v17, %v17, %v18
+;;       vlgvg   %r3, %v17, 0
+;;       vlgvg   %r2, %v17, 1
+;;       lmg     %r14, %r15, 0x110(%r15)
+;;       br      %r14
+;;
+;; wasm[0]::function[1]::sub128:
+;;       lg      %r1, 8(%r2)
+;;       lg      %r1, 0(%r1)
+;;       la      %r1, 0xa0(%r1)
+;;       clgrtle %r15, %r1
+;;       stmg    %r14, %r15, 0x70(%r15)
+;;       lgr     %r1, %r15
+;;       aghi    %r15, -0xa0
+;;       stg     %r1, 0(%r15)
+;;       vlvgp   %v17, %r5, %r4
+;;       vlvgp   %v18, %r7, %r6
+;;       vsq     %v17, %v17, %v18
+;;       vlgvg   %r3, %v17, 0
+;;       vlgvg   %r2, %v17, 1
+;;       lmg     %r14, %r15, 0x110(%r15)
+;;       br      %r14
+;;
+;; wasm[0]::function[2]::signed:
+;;       lg      %r1, 8(%r2)
+;;       lg      %r1, 0(%r1)
+;;       la      %r1, 0xa0(%r1)
+;;       clgrtle %r15, %r1
+;;       stmg    %r14, %r15, 0x70(%r15)
+;;       lgr     %r1, %r15
+;;       aghi    %r15, -0xa0
+;;       stg     %r1, 0(%r15)
+;;       mgrk    %r2, %r4, %r5
+;;       vlvgp   %v16, %r2, %r3
+;;       vlgvg   %r3, %v16, 0
+;;       vlgvg   %r2, %v16, 1
+;;       lmg     %r14, %r15, 0x110(%r15)
+;;       br      %r14
+;;
+;; wasm[0]::function[3]::unsigned:
+;;       lg      %r1, 8(%r2)
+;;       lg      %r1, 0(%r1)
+;;       la      %r1, 0xa0(%r1)
+;;       clgrtle %r15, %r1
+;;       stmg    %r14, %r15, 0x70(%r15)
+;;       lgr     %r1, %r15
+;;       aghi    %r15, -0xa0
+;;       stg     %r1, 0(%r15)
+;;       lgr     %r3, %r4
+;;       mlgr    %r2, %r5
+;;       vlvgp   %v16, %r2, %r3
+;;       vlgvg   %r3, %v16, 0
+;;       vlgvg   %r2, %v16, 1
+;;       lmg     %r14, %r15, 0x110(%r15)
+;;       br      %r14
+;;
+;; wasm[0]::function[4]::signed_only_high:
+;;       lg      %r1, 8(%r2)
+;;       lg      %r1, 0(%r1)
+;;       la      %r1, 0xa0(%r1)
+;;       clgrtle %r15, %r1
+;;       stmg    %r14, %r15, 0x70(%r15)
+;;       lgr     %r1, %r15
+;;       aghi    %r15, -0xa0
+;;       stg     %r1, 0(%r15)
+;;       mgrk    %r2, %r4, %r5
+;;       vlvgp   %v16, %r2, %r3
+;;       vlgvg   %r2, %v16, 0
+;;       vlgvg   %r3, %v16, 1
+;;       lmg     %r14, %r15, 0x110(%r15)
+;;       br      %r14
+;;
+;; wasm[0]::function[5]::unsigned_only_high:
+;;       lg      %r1, 8(%r2)
+;;       lg      %r1, 0(%r1)
+;;       la      %r1, 0xa0(%r1)
+;;       clgrtle %r15, %r1
+;;       stmg    %r14, %r15, 0x70(%r15)
+;;       lgr     %r1, %r15
+;;       aghi    %r15, -0xa0
+;;       stg     %r1, 0(%r15)
+;;       lgr     %r3, %r4
+;;       mlgr    %r2, %r5
+;;       vlvgp   %v16, %r2, %r3
+;;       vlgvg   %r2, %v16, 0
+;;       vlgvg   %r3, %v16, 1
+;;       lmg     %r14, %r15, 0x110(%r15)
+;;       br      %r14
diff --git a/tests/disas/x64-wide-arithmetic.wat b/tests/disas/x64-wide-arithmetic.wat
new file mode 100644
index 000000000..cd15c69a3
--- /dev/null
+++ b/tests/disas/x64-wide-arithmetic.wat
@@ -0,0 +1,105 @@
+;;! target = "x86_64"
+;;! test = "compile"
+;;! flags = "-Wwide-arithmetic"
+
+(module
+  (func $add128 (param i64 i64 i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    local.get 2
+    local.get 3
+    i64.add128)
+
+  (func $sub128 (param i64 i64 i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    local.get 2
+    local.get 3
+    i64.sub128)
+
+  (func $signed (param i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_s)
+
+  (func $unsigned (param i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_u)
+
+  (func $signed_only_high (param i64 i64) (result i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_s
+    local.set 0
+    drop
+    local.get 0)
+
+  (func $unsigned_only_high (param i64 i64) (result i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_u
+    local.set 0
+    drop
+    local.get 0)
+)
+
+;; wasm[0]::function[0]::add128:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    %rdx, %rax
+;;       addq    %r8, %rax
+;;       adcq    %r9, %rcx
+;;       movq    %rbp, %rsp
+;;       popq    %rbp
+;;       retq
+;;
+;; wasm[0]::function[1]::sub128:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    %rdx, %rax
+;;       subq    %r8, %rax
+;;       sbbq    %r9, %rcx
+;;       movq    %rbp, %rsp
+;;       popq    %rbp
+;;       retq
+;;
+;; wasm[0]::function[2]::signed:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    %rdx, %rax
+;;       imulq   %rcx
+;;       movq    %rdx, %rcx
+;;       movq    %rbp, %rsp
+;;       popq    %rbp
+;;       retq
+;;
+;; wasm[0]::function[3]::unsigned:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    %rdx, %rax
+;;       mulq    %rcx
+;;       movq    %rdx, %rcx
+;;       movq    %rbp, %rsp
+;;       popq    %rbp
+;;       retq
+;;
+;; wasm[0]::function[4]::signed_only_high:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    %rdx, %rax
+;;       imulq   %rcx
+;;       movq    %rdx, %rax
+;;       movq    %rbp, %rsp
+;;       popq    %rbp
+;;       retq
+;;
+;; wasm[0]::function[5]::unsigned_only_high:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    %rdx, %rax
+;;       mulq    %rcx
+;;       movq    %rdx, %rax
+;;       movq    %rbp, %rsp
+;;       popq    %rbp
+;;       retq
diff --git a/tests/misc_testsuite/wide-arithmetic.wast b/tests/misc_testsuite/wide-arithmetic.wast
new file mode 100644
index 000000000..930832375
--- /dev/null
+++ b/tests/misc_testsuite/wide-arithmetic.wast
@@ -0,0 +1,323 @@
+(module
+  (func (export "i64.add128") (param i64 i64 i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    local.get 2
+    local.get 3
+    i64.add128)
+  (func (export "i64.sub128") (param i64 i64 i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    local.get 2
+    local.get 3
+    i64.sub128)
+  (func (export "i64.mul_wide_s") (param i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_s)
+  (func (export "i64.mul_wide_u") (param i64 i64) (result i64 i64)
+    local.get 0
+    local.get 1
+    i64.mul_wide_u)
+)
+
+;; simple addition
+(assert_return (invoke "i64.add128"
+                  (i64.const 0) (i64.const 0)
+                  (i64.const 0) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.add128"
+                  (i64.const 0) (i64.const 1)
+                  (i64.const 1) (i64.const 0))
+               (i64.const 1) (i64.const 1))
+(assert_return (invoke "i64.add128"
+                  (i64.const 1) (i64.const 0)
+                  (i64.const -1) (i64.const 0))
+               (i64.const 0) (i64.const 1))
+(assert_return (invoke "i64.add128"
+                  (i64.const 1) (i64.const 1)
+                  (i64.const -1) (i64.const -1))
+               (i64.const 0) (i64.const 1))
+
+;; simple subtraction
+(assert_return (invoke "i64.sub128"
+                  (i64.const 0) (i64.const 0)
+                  (i64.const 0) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.sub128"
+                  (i64.const 0) (i64.const 0)
+                  (i64.const 1) (i64.const 0))
+               (i64.const -1) (i64.const -1))
+(assert_return (invoke "i64.sub128"
+                  (i64.const 0) (i64.const 1)
+                  (i64.const 1) (i64.const 1))
+               (i64.const -1) (i64.const -1))
+(assert_return (invoke "i64.sub128"
+                  (i64.const 0) (i64.const 0)
+                  (i64.const 1) (i64.const 1))
+               (i64.const -1) (i64.const -2))
+
+;; simple mul_wide
+(assert_return (invoke "i64.mul_wide_s" (i64.const 0) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 0) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 1) (i64.const 1))
+               (i64.const 1) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 1) (i64.const 1))
+               (i64.const 1) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const -1) (i64.const -1))
+               (i64.const 1) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const -1) (i64.const 1))
+               (i64.const -1) (i64.const -1))
+(assert_return (invoke "i64.mul_wide_u" (i64.const -1) (i64.const 1))
+               (i64.const -1) (i64.const 0))
+
+;; 20 randomly generated test cases for i64.add128
+(assert_return (invoke "i64.add128"
+                   (i64.const -2418420703207364752) (i64.const -1)
+                   (i64.const -1) (i64.const -1))
+               (i64.const -2418420703207364753) (i64.const -1))
+(assert_return (invoke "i64.add128"
+                   (i64.const 0) (i64.const 0)
+                   (i64.const -4579433644172935106) (i64.const -1))
+               (i64.const -4579433644172935106) (i64.const -1))
+(assert_return (invoke "i64.add128"
+                   (i64.const 0) (i64.const 0)
+                   (i64.const 1) (i64.const -1))
+               (i64.const 1) (i64.const -1))
+(assert_return (invoke "i64.add128"
+                   (i64.const 1) (i64.const 0)
+                   (i64.const 1) (i64.const 0))
+               (i64.const 2) (i64.const 0))
+(assert_return (invoke "i64.add128"
+                   (i64.const -1) (i64.const -1)
+                   (i64.const -1) (i64.const -1))
+               (i64.const -2) (i64.const -1))
+(assert_return (invoke "i64.add128"
+                   (i64.const 0) (i64.const -1)
+                   (i64.const 1) (i64.const 0))
+               (i64.const 1) (i64.const -1))
+(assert_return (invoke "i64.add128"
+                   (i64.const 0) (i64.const 0)
+                   (i64.const 0) (i64.const -1))
+               (i64.const 0) (i64.const -1))
+(assert_return (invoke "i64.add128"
+                   (i64.const 1) (i64.const 0)
+                   (i64.const -1) (i64.const -1))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.add128"
+                   (i64.const 0) (i64.const 6184727276166606191)
+                   (i64.const 0) (i64.const 1))
+               (i64.const 0) (i64.const 6184727276166606192))
+(assert_return (invoke "i64.add128"
+                   (i64.const -8434911321912688222) (i64.const -1)
+                   (i64.const 1) (i64.const -1))
+               (i64.const -8434911321912688221) (i64.const -2))
+(assert_return (invoke "i64.add128"
+                   (i64.const 1) (i64.const -1)
+                   (i64.const 0) (i64.const -1))
+               (i64.const 1) (i64.const -2))
+(assert_return (invoke "i64.add128"
+                   (i64.const 1) (i64.const -5148941131328838092)
+                   (i64.const 0) (i64.const 0))
+               (i64.const 1) (i64.const -5148941131328838092))
+(assert_return (invoke "i64.add128"
+                   (i64.const 1) (i64.const 1)
+                   (i64.const 1) (i64.const 0))
+               (i64.const 2) (i64.const 1))
+(assert_return (invoke "i64.add128"
+                   (i64.const -1) (i64.const -1)
+                   (i64.const -3636740005180858631) (i64.const -1))
+               (i64.const -3636740005180858632) (i64.const -1))
+(assert_return (invoke "i64.add128"
+                   (i64.const -5529682780229988275) (i64.const -1)
+                   (i64.const 0) (i64.const 0))
+               (i64.const -5529682780229988275) (i64.const -1))
+(assert_return (invoke "i64.add128"
+                   (i64.const 1) (i64.const -5381447440966559717)
+                   (i64.const 1020031372481336745) (i64.const 1))
+               (i64.const 1020031372481336746) (i64.const -5381447440966559716))
+(assert_return (invoke "i64.add128"
+                   (i64.const 1) (i64.const 1)
+                   (i64.const 0) (i64.const 0))
+               (i64.const 1) (i64.const 1))
+(assert_return (invoke "i64.add128"
+                   (i64.const -9133888546939907356) (i64.const -1)
+                   (i64.const 1) (i64.const 1))
+               (i64.const -9133888546939907355) (i64.const 0))
+(assert_return (invoke "i64.add128"
+                   (i64.const -4612047512704241719) (i64.const -1)
+                   (i64.const 0) (i64.const -1))
+               (i64.const -4612047512704241719) (i64.const -2))
+(assert_return (invoke "i64.add128"
+                   (i64.const 414720966820876428) (i64.const -1)
+                   (i64.const 1) (i64.const 0))
+               (i64.const 414720966820876429) (i64.const -1))
+
+
+;; 20 randomly generated test cases for i64.sub128
+(assert_return (invoke "i64.sub128"
+                   (i64.const 0) (i64.const -2459085471354756766)
+                   (i64.const -9151153060221070927) (i64.const -1))
+               (i64.const 9151153060221070927) (i64.const -2459085471354756766))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 4566502638724063423) (i64.const -4282658540409485563)
+                   (i64.const -6884077310018979971) (i64.const -1))
+               (i64.const -6996164124966508222) (i64.const -4282658540409485563))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 1) (i64.const 3118380319444903041)
+                   (i64.const 0) (i64.const 3283115686417695443))
+               (i64.const 1) (i64.const -164735366972792402))
+(assert_return (invoke "i64.sub128"
+                   (i64.const -7208415241680161810) (i64.const -1)
+                   (i64.const 1) (i64.const 0))
+               (i64.const -7208415241680161811) (i64.const -1))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 0) (i64.const 3944850126731328706)
+                   (i64.const 1) (i64.const 1))
+               (i64.const -1) (i64.const 3944850126731328704))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 1) (i64.const -1)
+                   (i64.const -1) (i64.const -1))
+               (i64.const 2) (i64.const -1))
+(assert_return (invoke "i64.sub128"
+                   (i64.const -1) (i64.const -1)
+                   (i64.const 4855833073346115923) (i64.const -6826437637438999645))
+               (i64.const -4855833073346115924) (i64.const 6826437637438999644))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 1) (i64.const 0)
+                   (i64.const -1) (i64.const -1))
+               (i64.const 2) (i64.const 0))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 1) (i64.const 0)
+                   (i64.const 1) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.sub128"
+                   (i64.const -1) (i64.const -1)
+                   (i64.const 0) (i64.const 0))
+               (i64.const -1) (i64.const -1))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 1) (i64.const -1)
+                   (i64.const -6365475388498096428) (i64.const -1))
+               (i64.const 6365475388498096429) (i64.const -1))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 6804238617560992346) (i64.const -1)
+                   (i64.const 0) (i64.const -1))
+               (i64.const 6804238617560992346) (i64.const 0))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 0) (i64.const 1)
+                   (i64.const 1) (i64.const -7756145513466453619))
+               (i64.const -1) (i64.const 7756145513466453619))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 1) (i64.const -1)
+                   (i64.const 1) (i64.const 1))
+               (i64.const 0) (i64.const -2))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 0) (i64.const 1)
+                   (i64.const 1) (i64.const 0))
+               (i64.const -1) (i64.const 0))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 1) (i64.const 5602881641763648953)
+                   (i64.const -2110589244314239080) (i64.const -1))
+               (i64.const 2110589244314239081) (i64.const 5602881641763648953))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 0) (i64.const 1)
+                   (i64.const -1) (i64.const -1))
+               (i64.const 1) (i64.const 1))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 0) (i64.const -1)
+                   (i64.const 3553816990259121806) (i64.const -2105235417856431622))
+               (i64.const -3553816990259121806) (i64.const 2105235417856431620))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 1861102705894987245) (i64.const 1)
+                   (i64.const 3713781778534059871) (i64.const 1))
+               (i64.const -1852679072639072626) (i64.const -1))
+(assert_return (invoke "i64.sub128"
+                   (i64.const 0) (i64.const -1)
+                   (i64.const 1) (i64.const 1832524486821761762))
+               (i64.const -1) (i64.const -1832524486821761764))
+
+;; 20 randomly generated test cases for i64.mul_wide_s
+(assert_return (invoke "i64.mul_wide_s" (i64.const 1) (i64.const 1))
+               (i64.const 1) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 0) (i64.const 6287758211025156705))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const -6643537319803451357) (i64.const 1))
+               (i64.const -6643537319803451357) (i64.const -1))
+(assert_return (invoke "i64.mul_wide_s" (i64.const -2483565146858803428) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 1) (i64.const 1))
+               (i64.const 1) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const -3838951433439430085) (i64.const 3471602925362676030))
+               (i64.const 5186941893001237834) (i64.const -722475195264825124))
+(assert_return (invoke "i64.mul_wide_s" (i64.const -8262495286814853129) (i64.const 7883241869666573970))
+               (i64.const -8557189786755031842) (i64.const -3530988912334554469))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 4278371902407959701) (i64.const 1))
+               (i64.const 4278371902407959701) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const -8852706149487089182) (i64.const -1))
+               (i64.const 8852706149487089182) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 1) (i64.const -1))
+               (i64.const -1) (i64.const -1))
+(assert_return (invoke "i64.mul_wide_s" (i64.const -1) (i64.const -4329244561838653387))
+               (i64.const 4329244561838653387) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const -1) (i64.const -1))
+               (i64.const 1) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 697896157315764057) (i64.const 1))
+               (i64.const 697896157315764057) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 1) (i64.const 1))
+               (i64.const 1) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const -1) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 0) (i64.const -3769664482072947073))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 1) (i64.const 8414291037346403854))
+               (i64.const 8414291037346403854) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 1) (i64.const -1))
+               (i64.const -1) (i64.const -1))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 5014655679779318485) (i64.const -5080037812563681985))
+               (i64.const 2842857627777395563) (i64.const -1380983027057486843))
+(assert_return (invoke "i64.mul_wide_s" (i64.const 0) (i64.const 1))
+               (i64.const 0) (i64.const 0))
+
+;; 20 randomly generated test cases for i64.mul_wide_u
+(assert_return (invoke "i64.mul_wide_u" (i64.const -4734436040338162711) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 1) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 3270597527173764279) (i64.const 6636648075495406358))
+               (i64.const -5430303818902260550) (i64.const 1176674035141685826))
+(assert_return (invoke "i64.mul_wide_u" (i64.const -7771814344630108151) (i64.const 1))
+               (i64.const -7771814344630108151) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 1) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 1) (i64.const -7864138787704962081))
+               (i64.const -7864138787704962081) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 1) (i64.const 518555141550256010))
+               (i64.const 518555141550256010) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 1) (i64.const -1))
+               (i64.const -1) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 1118900477321231571) (i64.const -1))
+               (i64.const -1118900477321231571) (i64.const 1118900477321231570))
+(assert_return (invoke "i64.mul_wide_u" (i64.const -1) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const -5586890671027490027) (i64.const 1))
+               (i64.const -5586890671027490027) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 0) (i64.const 3603850799751152505))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const -1) (i64.const -1))
+               (i64.const 1) (i64.const 18446744073709551614))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 0) (i64.const 1))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const -7344082851774441644) (i64.const 3896439839137544024))
+               (i64.const 5738542512914895072) (i64.const 2345175459296971666))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 0) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 616395976148874061) (i64.const 0))
+               (i64.const 0) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 2810729703362889816) (i64.const -1))
+               (i64.const -2810729703362889816) (i64.const 2810729703362889815))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 1) (i64.const -1))
+               (i64.const -1) (i64.const 0))
+(assert_return (invoke "i64.mul_wide_u" (i64.const 1) (i64.const 0))
+               (i64.const 0) (i64.const 0))
diff --git a/tests/wast.rs b/tests/wast.rs
index e3f09012f..691e9c086 100644
--- a/tests/wast.rs
+++ b/tests/wast.rs
@@ -174,6 +174,8 @@ fn should_fail(test: &Path, strategy: Strategy) -> bool {
             "spec_testsuite/simd_store32_lane.wast",
             "spec_testsuite/simd_store64_lane.wast",
             "spec_testsuite/simd_store8_lane.wast",
+            // wide arithmetic
+            "misc_testsuite/wide-arithmetic.wast",
         ];
 
         if unsupported.iter().any(|part| test.ends_with(part)) {
@@ -281,6 +283,7 @@ fn run_wast(wast: &Path, strategy: Strategy, pooling: bool) -> anyhow::Result<()
     let use_shared_memory = feature_found_src(&wast_bytes, "shared_memory")
         || feature_found_src(&wast_bytes, "shared)");
     let extended_const = feature_found(wast, "extended-const") || memory64;
+    let wide_arithmetic = feature_found(wast, "wide-arithmetic");
 
     if pooling && use_shared_memory {
         log::warn!("skipping pooling test with shared memory");
@@ -303,6 +306,7 @@ fn run_wast(wast: &Path, strategy: Strategy, pooling: bool) -> anyhow::Result<()
         .wasm_tail_call(tail_call)
         .wasm_custom_page_sizes(custom_page_sizes)
         .wasm_extended_const(extended_const)
+        .wasm_wide_arithmetic(wide_arithmetic)
         .strategy(strategy);
 
     if is_cranelift {