Skip to content

Commit

Permalink
Implement the wide-arithmetic proposal
Browse files Browse the repository at this point in the history
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
  • Loading branch information
alexcrichton committed Oct 8, 2024
1 parent d51a921 commit 51e8a10
Show file tree
Hide file tree
Showing 18 changed files with 884 additions and 20 deletions.
8 changes: 8 additions & 0 deletions crates/c-api/include/wasmtime/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand Down
5 changes: 5 additions & 0 deletions crates/c-api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
5 changes: 5 additions & 0 deletions crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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))*) => ($(
Expand Down
38 changes: 31 additions & 7 deletions crates/cranelift/src/translate/code_translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand Down
6 changes: 6 additions & 0 deletions crates/cranelift/src/translate/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions crates/fuzzing/src/generators/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down
33 changes: 20 additions & 13 deletions crates/fuzzing/src/generators/single_inst_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down Expand Up @@ -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)]
Expand Down
1 change: 1 addition & 0 deletions crates/fuzzing/src/oracles/diff_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
1 change: 1 addition & 0 deletions crates/fuzzing/src/oracles/diff_v8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()))),
Expand Down
1 change: 1 addition & 0 deletions crates/fuzzing/src/oracles/diff_wasmi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
11 changes: 11 additions & 0 deletions crates/wasmtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
2 changes: 2 additions & 0 deletions docs/stability-tiers.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,15 @@ 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
[`threads`]: https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md
[`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

Expand Down
91 changes: 91 additions & 0 deletions tests/disas/aarch64-wide-arithmetic.wat
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 51e8a10

Please sign in to comment.