934: In LLVM backend, track which floats are guaranteed to be arithmetic, which makes the canonicalization a no-op. r=nlewycky a=nlewycky

# Description
This is a reimplementation of the patch in PR #651.

Extend state.rs ExtraInfo to track more information about floats. In addition to tracking whether the value has a pending canonicalization of NaNs, also track whether the value is known to be arithmetic (which includes infinities, regular values, and non-signalling NaNs (aka. "arithmetic NaNs" in the webassembly spec)). When the value is arithmetic, the correct sequence of operations to canonicalize the value is a no-op. Therefore, we create a lattice where pending+arithmetic=arithmetic.

Also, this extends the tracking to track all values, including non-SIMD integers. That's why there are more places where pending canonicalizations are applied.

Looking at c-wasm-simd128-example, this provides no performance change to the non-SIMD case (takes 58s on my noisy dev machine). The SIMD case drops from 46s to 29s.

# Review

- [ ] Add a short description of the the change to the CHANGELOG.md file


Co-authored-by: Nick Lewycky <nick@wasmer.io>
This commit is contained in:
bors[bot] 2019-11-26 20:29:31 +00:00 committed by GitHub
commit 53f0a9cecf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 965 additions and 497 deletions

View File

@ -13,6 +13,7 @@
- [#990](https://github.com/wasmerio/wasmer/pull/990) Default wasmer CLI to `run`. Wasmer will now attempt to parse unrecognized command line options as if they were applied to the run command: `wasmer mywasm.wasm --dir=.` now works!
- [#987](https://github.com/wasmerio/wasmer/pull/987) Fix `runtime-c-api` header files when compiled by gnuc.
- [#957](https://github.com/wasmerio/wasmer/pull/957) Change the meaning of `wasmer_wasi::is_wasi_module` to detect any type of WASI module, add support for new wasi snapshot_preview1
- [#934](https://github.com/wasmerio/wasmer/pull/934) Simplify float expressions in the LLVM backend.
## 0.10.2 - 2019-11-18

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@ use inkwell::{
};
use smallvec::SmallVec;
use std::cell::Cell;
use std::ops::{BitAnd, BitOr, BitOrAssign};
use wasmparser::BinaryReaderError;
#[derive(Debug)]
@ -67,23 +68,127 @@ impl ControlFrame {
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub enum ExtraInfo {
None,
// This values is required to be arithmetic 32-bit NaN (or 32x4) by the WAsm
// machine, but which might not be in the LLVM value. The conversion to
// arithmetic NaN is pending. It is required for correctness.
PendingF32NaN,
// This values is required to be arithmetic 64-bit NaN (or 64x2) by the WAsm
// machine, but which might not be in the LLVM value. The conversion to
// arithmetic NaN is pending. It is required for correctness.
PendingF64NaN,
#[derive(Debug, Default, Eq, PartialEq, Copy, Clone, Hash)]
pub struct ExtraInfo {
state: u8,
}
impl Default for ExtraInfo {
fn default() -> Self {
ExtraInfo::None
impl ExtraInfo {
// This value is required to be arithmetic 32-bit NaN (or 32x4) by the WAsm
// machine, but which might not be in the LLVM value. The conversion to
// arithmetic NaN is pending. It is required for correctness.
//
// When applied to a 64-bit value, this flag has no meaning and must be
// ignored. It may be set in such cases to allow for common handling of
// 32 and 64-bit operations.
pub const fn pending_f32_nan() -> ExtraInfo {
ExtraInfo { state: 1 }
}
// This value is required to be arithmetic 64-bit NaN (or 64x2) by the WAsm
// machine, but which might not be in the LLVM value. The conversion to
// arithmetic NaN is pending. It is required for correctness.
//
// When applied to a 32-bit value, this flag has no meaning and must be
// ignored. It may be set in such cases to allow for common handling of
// 32 and 64-bit operations.
pub const fn pending_f64_nan() -> ExtraInfo {
ExtraInfo { state: 2 }
}
// This value either does not contain a 32-bit NaN, or it contains an
// arithmetic NaN. In SIMD, applies to all 4 lanes.
pub const fn arithmetic_f32() -> ExtraInfo {
ExtraInfo { state: 4 }
}
// This value either does not contain a 64-bit NaN, or it contains an
// arithmetic NaN. In SIMD, applies to both lanes.
pub const fn arithmetic_f64() -> ExtraInfo {
ExtraInfo { state: 8 }
}
pub const fn has_pending_f32_nan(&self) -> bool {
self.state & ExtraInfo::pending_f32_nan().state != 0
}
pub const fn has_pending_f64_nan(&self) -> bool {
self.state & ExtraInfo::pending_f64_nan().state != 0
}
pub const fn is_arithmetic_f32(&self) -> bool {
self.state & ExtraInfo::arithmetic_f32().state != 0
}
pub const fn is_arithmetic_f64(&self) -> bool {
self.state & ExtraInfo::arithmetic_f64().state != 0
}
pub const fn strip_pending(&self) -> ExtraInfo {
ExtraInfo {
state: self.state
& !(ExtraInfo::pending_f32_nan().state | ExtraInfo::pending_f64_nan().state),
}
}
}
// Union two ExtraInfos.
impl BitOr for ExtraInfo {
type Output = Self;
fn bitor(self, other: Self) -> Self {
debug_assert!(!(self.has_pending_f32_nan() && other.has_pending_f64_nan()));
debug_assert!(!(self.has_pending_f64_nan() && other.has_pending_f32_nan()));
ExtraInfo {
state: if self.is_arithmetic_f32() || other.is_arithmetic_f32() {
ExtraInfo::arithmetic_f32().state
} else if self.has_pending_f32_nan() || other.has_pending_f32_nan() {
ExtraInfo::pending_f32_nan().state
} else {
0
} + if self.is_arithmetic_f64() || other.is_arithmetic_f64() {
ExtraInfo::arithmetic_f64().state
} else if self.has_pending_f64_nan() || other.has_pending_f64_nan() {
ExtraInfo::pending_f64_nan().state
} else {
0
},
}
}
}
impl BitOrAssign for ExtraInfo {
fn bitor_assign(&mut self, other: Self) {
*self = *self | other;
}
}
// Intersection for ExtraInfo.
impl BitAnd for ExtraInfo {
type Output = Self;
fn bitand(self, other: Self) -> Self {
// Pending canonicalizations are not safe to discard, or even reorder.
assert!(
self.has_pending_f32_nan() == other.has_pending_f32_nan()
|| self.is_arithmetic_f32()
|| other.is_arithmetic_f32()
);
assert!(
self.has_pending_f64_nan() == other.has_pending_f64_nan()
|| self.is_arithmetic_f64()
|| other.is_arithmetic_f64()
);
let info = match (
self.is_arithmetic_f32() && other.is_arithmetic_f32(),
self.is_arithmetic_f64() && other.is_arithmetic_f64(),
) {
(false, false) => Default::default(),
(true, false) => ExtraInfo::arithmetic_f32(),
(false, true) => ExtraInfo::arithmetic_f64(),
(true, true) => ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(),
};
let info = match (self.has_pending_f32_nan(), self.has_pending_f64_nan()) {
(false, false) => info,
(true, false) => info | ExtraInfo::pending_f32_nan(),
(false, true) => info | ExtraInfo::pending_f64_nan(),
(true, true) => unreachable!("Can't form ExtraInfo with two pending canonicalizations"),
};
info
}
}
@ -165,7 +270,7 @@ impl State {
}
pub fn push1<T: BasicValue>(&mut self, value: T) {
self.push1_extra(value, ExtraInfo::None);
self.push1_extra(value, Default::default());
}
pub fn push1_extra<T: BasicValue>(&mut self, value: T, info: ExtraInfo) {
@ -197,15 +302,6 @@ impl State {
Ok((v1, v2))
}
pub fn pop3(
&mut self,
) -> Result<(BasicValueEnum, BasicValueEnum, BasicValueEnum), BinaryReaderError> {
let v3 = self.pop1()?;
let v2 = self.pop1()?;
let v1 = self.pop1()?;
Ok((v1, v2, v3))
}
pub fn pop3_extra(
&mut self,
) -> Result<