Skip to content

Commit

Permalink
Centralize handling of float operations
Browse files Browse the repository at this point in the history
Add a new `wasmtime-math` crate which is tasked with providing the float
math functions needed by Wasm with with, in theory, most optimal
platform implementation available.

prtest:full
  • Loading branch information
alexcrichton committed Dec 13, 2024
1 parent 0b113f5 commit 2d11ac3
Show file tree
Hide file tree
Showing 11 changed files with 357 additions and 513 deletions.
11 changes: 9 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ wasi-common = { path = "crates/wasi-common", version = "=29.0.0", default-featur
wasmtime-fuzzing = { path = "crates/fuzzing" }
wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=29.0.0" }
wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=29.0.0" }
wasmtime-math = { path = "crates/math", version = "=29.0.0" }
test-programs-artifacts = { path = 'crates/test-programs/artifacts' }

pulley-interpreter = { path = 'pulley', version = "=29.0.0" }
Expand Down
23 changes: 23 additions & 0 deletions crates/math/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "wasmtime-math"
version.workspace = true
authors.workspace = true
description = "Low-level math routines used in Wasmtime"
documentation = "https://docs.rs/wasmtime-math"
license = "Apache-2.0 WITH LLVM-exception"
repository = "https://github.com/bytecodealliance/wasmtime"
readme = "README.md"
edition.workspace = true
rust-version.workspace = true

[lints]
workspace = true

[package.metadata.docs.rs]
all-features = true

[features]
std = []

[dependencies]
libm = { workspace = true }
279 changes: 279 additions & 0 deletions crates/math/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
//! A minimal helper crate for implementing float-related operations for
//! WebAssembly in terms of the native platform primitives.
//!
//! This crate is intended to assist with solving the portability issues such
//! as:
//!
//! * Functions like `f32::trunc` are not available in `#![no_std]` targets.
//! * The `f32::trunc` function is likely faster than the `libm` fallback.
//! * Behavior of `f32::trunc` differs across platforms, for example it's
//! different on Windows and glibc on Linux.
//! * Some wasm functions are in the Rust standard library, but not stable yet.
//!
//! There are a few locations throughout the codebase that these functions are
//! needed so they're implemented only in a single location here rather than
//! multiple.
#![no_std]

#[cfg(feature = "std")]
extern crate std;

pub trait WasmFloat {
fn wasm_trunc(self) -> Self;
fn wasm_copysign(self, sign: Self) -> Self;
fn wasm_floor(self) -> Self;
fn wasm_ceil(self) -> Self;
fn wasm_sqrt(self) -> Self;
fn wasm_abs(self) -> Self;
fn wasm_nearest(self) -> Self;
fn wasm_minimum(self, other: Self) -> Self;
fn wasm_maximum(self, other: Self) -> Self;
fn mul_add(self, b: Self, c: Self) -> Self;
}

impl WasmFloat for f32 {
#[inline]
fn wasm_trunc(self) -> f32 {
#[cfg(feature = "std")]
if !cfg!(windows) {
return self.trunc();
}
if self.is_nan() {
return f32::NAN;
}
libm::truncf(self)
}
#[inline]
fn wasm_copysign(self, sign: f32) -> f32 {
#[cfg(feature = "std")]
if true {
return self.copysign(sign);
}
libm::copysignf(self, sign)
}
#[inline]
fn wasm_floor(self) -> f32 {
#[cfg(feature = "std")]
if true {
return self.floor();
}
if self.is_nan() {
return f32::NAN;
}
libm::floorf(self)
}
#[inline]
fn wasm_ceil(self) -> f32 {
#[cfg(feature = "std")]
if true {
return self.ceil();
}
if self.is_nan() {
return f32::NAN;
}
libm::ceilf(self)
}
#[inline]
fn wasm_sqrt(self) -> f32 {
#[cfg(feature = "std")]
if true {
return self.sqrt();
}
libm::sqrtf(self)
}
#[inline]
fn wasm_abs(self) -> f32 {
#[cfg(feature = "std")]
if true {
return self.abs();
}
libm::fabsf(self)
}
#[inline]
fn wasm_nearest(self) -> f32 {
#[cfg(feature = "std")]
if !cfg!(windows) {
return self.round_ties_even();
}
if self.is_nan() {
return f32::NAN;
}
let round = self.round();
if (self - round).abs() != 0.5 {
return round;
}
match round % 2.0 {
1.0 => self.floor(),
-1.0 => self.ceil(),
_ => round,
}
}
#[inline]
fn wasm_maximum(self, other: f32) -> f32 {
// FIXME: replace this with `a.maximum(b)` when rust-lang/rust#91079 is
// stabilized
if self > other {
self
} else if other > self {
other
} else if self == other {
if self.is_sign_positive() && other.is_sign_negative() {
self
} else {
other
}
} else {
self + other
}
}
#[inline]
fn wasm_minimum(self, other: f32) -> f32 {
// FIXME: replace this with `self.minimum(other)` when
// rust-lang/rust#91079 is stabilized
if self < other {
self
} else if other < self {
other
} else if self == other {
if self.is_sign_negative() && other.is_sign_positive() {
self
} else {
other
}
} else {
self + other
}
}
#[inline]
fn mul_add(self, b: f32, c: f32) -> f32 {
#[cfg(feature = "std")]
if true {
return self.mul_add(b, c);
}
libm::fmaf(self, b, c)
}
}

impl WasmFloat for f64 {
#[inline]
fn wasm_trunc(self) -> f64 {
#[cfg(feature = "std")]
if !cfg!(windows) {
return self.trunc();
}
if self.is_nan() {
return f64::NAN;
}
libm::trunc(self)
}
#[inline]
fn wasm_copysign(self, sign: f64) -> f64 {
#[cfg(feature = "std")]
if true {
return self.copysign(sign);
}
libm::copysign(self, sign)
}
#[inline]
fn wasm_floor(self) -> f64 {
#[cfg(feature = "std")]
if true {
return self.floor();
}
if self.is_nan() {
return f64::NAN;
}
libm::floor(self)
}
#[inline]
fn wasm_ceil(self) -> f64 {
#[cfg(feature = "std")]
if true {
return self.ceil();
}
if self.is_nan() {
return f64::NAN;
}
libm::ceil(self)
}
#[inline]
fn wasm_sqrt(self) -> f64 {
#[cfg(feature = "std")]
if true {
return self.sqrt();
}
libm::sqrt(self)
}
#[inline]
fn wasm_abs(self) -> f64 {
#[cfg(feature = "std")]
if true {
return self.abs();
}
libm::fabs(self)
}
#[inline]
fn wasm_nearest(self) -> f64 {
#[cfg(feature = "std")]
if !cfg!(windows) {
return self.round_ties_even();
}
if self.is_nan() {
return f64::NAN;
}
let round = self.round();
if (self - round).abs() != 0.5 {
return round;
}
match round % 2.0 {
1.0 => self.floor(),
-1.0 => self.ceil(),
_ => round,
}
}
#[inline]
fn wasm_maximum(self, other: f64) -> f64 {
// FIXME: replace this with `a.maximum(b)` when rust-lang/rust#91079 is
// stabilized
if self > other {
self
} else if other > self {
other
} else if self == other {
if self.is_sign_positive() && other.is_sign_negative() {
self
} else {
other
}
} else {
self + other
}
}
#[inline]
fn wasm_minimum(self, other: f64) -> f64 {
// FIXME: replace this with `self.minimum(other)` when
// rust-lang/rust#91079 is stabilized
if self < other {
self
} else if other < self {
other
} else if self == other {
if self.is_sign_negative() && other.is_sign_positive() {
self
} else {
other
}
} else {
self + other
}
}
#[inline]
fn mul_add(self, b: f64, c: f64) -> f64 {
#[cfg(feature = "std")]
if true {
return self.mul_add(b, c);
}
libm::fma(self, b, c)
}
}
3 changes: 2 additions & 1 deletion crates/wasmtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ wasmtime-component-util = { workspace = true, optional = true }
wasmtime-slab = { workspace = true, optional = true }
wasmtime-versioned-export-macros = { workspace = true }
wasmtime-wmemcheck = { workspace = true, optional = true }
wasmtime-math = { workspace = true }
pulley-interpreter = { workspace = true, optional = true }
target-lexicon = { workspace = true }
wasmparser = { workspace = true }
Expand Down Expand Up @@ -59,7 +60,6 @@ addr2line = { workspace = true, optional = true }
semver = { workspace = true, optional = true }
smallvec = { workspace = true, optional = true }
hashbrown = { workspace = true, features = ["ahash"] }
libm = { workspace = true }
bitflags = { workspace = true }

[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
Expand Down Expand Up @@ -316,6 +316,7 @@ std = [
'once_cell',
'wasmtime-fiber?/std',
'pulley-interpreter?/std',
'wasmtime-math/std',
# technically this isn't necessary but once you have the standard library you
# probably want things to go fast in which case you've probably got signal
# handlers and such so implicitly enable this. This also helps reduce the
Expand Down
Loading

0 comments on commit 2d11ac3

Please sign in to comment.