Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support non-trapping-float-to-int-conversions Wasm proposal #359

Merged
merged 4 commits into from
Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub use self::{
LittleEndianConvert,
SignExtendFrom,
TransmuteInto,
TruncateSaturateInto,
TryTruncateInto,
Value,
ValueType,
Expand Down
44 changes: 44 additions & 0 deletions core/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,32 @@ pub trait WrapInto<T> {
}

/// Convert one type to another by rounding to the nearest integer towards zero.
///
/// # Errors
///
/// Traps when the input float cannot be represented by the target integer or
/// when the input float is NaN.
pub trait TryTruncateInto<T, E> {
/// Convert one type to another by rounding to the nearest integer towards zero.
fn try_truncate_into(self) -> Result<T, E>;
}

/// Convert one type to another by rounding to the nearest integer towards zero.
///
/// # Note
///
/// This has saturating semantics for when the integer cannot represent the float.
///
/// Returns
///
/// - `0` when the input is NaN.
/// - `int::MIN` when the input is -INF.
/// - `int::MAX` when the input is +INF.
pub trait TruncateSaturateInto<T> {
/// Convert one type to another by rounding to the nearest integer towards zero.
fn truncate_saturate_into(self) -> T;
}

/// Convert one type to another by extending with leading zeroes.
pub trait ExtendInto<T> {
/// Convert one type to another by extending with leading zeroes.
Expand Down Expand Up @@ -509,6 +530,22 @@ macro_rules! impl_try_truncate_into {
.ok_or(TrapCode::IntegerOverflow)
}
}

impl TruncateSaturateInto<$into> for $from {
#[inline]
fn truncate_saturate_into(self) -> $into {
if self.is_nan() {
return <$into as Default>::default();
}
if self.is_infinite() && self.is_sign_positive() {
return <$into>::MAX;
}
if self.is_infinite() && self.is_sign_negative() {
return <$into>::MIN;
}
self as _
}
}
};
(@wrapped $from:ident, $intermediate:ident, $into:ident) => {
impl TryTruncateInto<$into, TrapCode> for $from {
Expand All @@ -517,6 +554,13 @@ macro_rules! impl_try_truncate_into {
$intermediate::from(self).try_truncate_into()
}
}

impl TruncateSaturateInto<$into> for $from {
#[inline]
fn truncate_saturate_into(self) -> $into {
$intermediate::from(self).truncate_saturate_into()
}
}
};
}

Expand Down
8 changes: 8 additions & 0 deletions tests/spec/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ macro_rules! define_tests {
};
}

mod saturating_float_to_int {
use super::run_wasm_spec_test;

define_tests! {
fn wasm_conversions("proposals/nontrapping-float-to-int-conversions/conversions");
}
}

mod sign_extension_ops {
use super::run_wasm_spec_test;

Expand Down
8 changes: 8 additions & 0 deletions wasmi_v1/src/engine/bytecode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ pub enum Instruction {
I64ReinterpretF64,
F32ReinterpretI32,
F64ReinterpretI64,
I32TruncSatF32S,
I32TruncSatF32U,
I32TruncSatF64S,
I32TruncSatF64U,
I64TruncSatF32S,
I64TruncSatF32U,
I64TruncSatF64S,
I64TruncSatF64U,
I32Extend8S,
I32Extend16S,
I64Extend8S,
Expand Down
8 changes: 8 additions & 0 deletions wasmi_v1/src/engine/bytecode/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ pub trait VisitInstruction {
fn visit_i64_reinterpret_f64(&mut self) -> Self::Outcome;
fn visit_f32_reinterpret_i32(&mut self) -> Self::Outcome;
fn visit_f64_reinterpret_i64(&mut self) -> Self::Outcome;
fn visit_i32_trunc_sat_f32(&mut self) -> Self::Outcome;
fn visit_u32_trunc_sat_f32(&mut self) -> Self::Outcome;
fn visit_i32_trunc_sat_f64(&mut self) -> Self::Outcome;
fn visit_u32_trunc_sat_f64(&mut self) -> Self::Outcome;
fn visit_i64_trunc_sat_f32(&mut self) -> Self::Outcome;
fn visit_u64_trunc_sat_f32(&mut self) -> Self::Outcome;
fn visit_i64_trunc_sat_f64(&mut self) -> Self::Outcome;
fn visit_u64_trunc_sat_f64(&mut self) -> Self::Outcome;
fn visit_i32_sign_extend8(&mut self) -> Self::Outcome;
fn visit_i32_sign_extend16(&mut self) -> Self::Outcome;
fn visit_i64_sign_extend8(&mut self) -> Self::Outcome;
Expand Down
8 changes: 8 additions & 0 deletions wasmi_v1/src/engine/code_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,14 @@ impl<'a> ResolvedFuncBody<'a> {
Instruction::I64ReinterpretF64 => visitor.visit_i64_reinterpret_f64(),
Instruction::F32ReinterpretI32 => visitor.visit_f32_reinterpret_i32(),
Instruction::F64ReinterpretI64 => visitor.visit_f64_reinterpret_i64(),
Instruction::I32TruncSatF32S => visitor.visit_i32_trunc_sat_f32(),
Instruction::I32TruncSatF32U => visitor.visit_u32_trunc_sat_f32(),
Instruction::I32TruncSatF64S => visitor.visit_i32_trunc_sat_f64(),
Instruction::I32TruncSatF64U => visitor.visit_u32_trunc_sat_f64(),
Instruction::I64TruncSatF32S => visitor.visit_i64_trunc_sat_f32(),
Instruction::I64TruncSatF32U => visitor.visit_u64_trunc_sat_f32(),
Instruction::I64TruncSatF64S => visitor.visit_i64_trunc_sat_f64(),
Instruction::I64TruncSatF64U => visitor.visit_u64_trunc_sat_f64(),
Instruction::I32Extend8S => visitor.visit_i32_sign_extend8(),
Instruction::I32Extend16S => visitor.visit_i32_sign_extend16(),
Instruction::I64Extend8S => visitor.visit_i64_sign_extend8(),
Expand Down
44 changes: 44 additions & 0 deletions wasmi_v1/src/engine/exec_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use wasmi_core::{
Integer,
LittleEndianConvert,
SignExtendFrom,
TruncateSaturateInto,
TryTruncateInto,
WrapInto,
};
Expand Down Expand Up @@ -647,6 +648,17 @@ where
Ok(ExecutionOutcome::Continue)
}

fn execute_trunc_sat_to_int<T, U>(&mut self) -> Result<ExecutionOutcome, Trap>
where
StackEntry: From<U>,
T: TruncateSaturateInto<U> + FromStackEntry,
{
let entry = self.value_stack.last_mut();
let value = T::from_stack_entry(*entry).truncate_saturate_into();
*entry = value.into();
Ok(ExecutionOutcome::Continue)
}

fn execute_reinterpret<T, U>(&mut self) -> Result<ExecutionOutcome, Trap>
where
StackEntry: From<U>,
Expand Down Expand Up @@ -1422,6 +1434,38 @@ where
self.execute_reinterpret::<i64, F64>()
}

fn visit_i32_trunc_sat_f32(&mut self) -> Self::Outcome {
self.execute_trunc_sat_to_int::<F32, i32>()
}

fn visit_u32_trunc_sat_f32(&mut self) -> Self::Outcome {
self.execute_trunc_sat_to_int::<F32, u32>()
}

fn visit_i32_trunc_sat_f64(&mut self) -> Self::Outcome {
self.execute_trunc_sat_to_int::<F64, i32>()
}

fn visit_u32_trunc_sat_f64(&mut self) -> Self::Outcome {
self.execute_trunc_sat_to_int::<F64, u32>()
}

fn visit_i64_trunc_sat_f32(&mut self) -> Self::Outcome {
self.execute_trunc_sat_to_int::<F32, i64>()
}

fn visit_u64_trunc_sat_f32(&mut self) -> Self::Outcome {
self.execute_trunc_sat_to_int::<F32, u64>()
}

fn visit_i64_trunc_sat_f64(&mut self) -> Self::Outcome {
self.execute_trunc_sat_to_int::<F64, i64>()
}

fn visit_u64_trunc_sat_f64(&mut self) -> Self::Outcome {
self.execute_trunc_sat_to_int::<F64, u64>()
}

fn visit_i32_sign_extend8(&mut self) -> Self::Outcome {
self.execute_sign_extend::<i32, i8>()
}
Expand Down
40 changes: 40 additions & 0 deletions wasmi_v1/src/engine/func_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1875,6 +1875,46 @@ impl<'engine, 'parser> FunctionBuilder<'engine, 'parser> {
)
}

/// Translate a Wasm `i32.truncate_sat_f32` instruction.
pub fn translate_i32_truncate_saturate_f32(&mut self) -> Result<(), ModuleError> {
self.translate_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncSatF32S)
}

/// Translate a Wasm `u32.truncate_sat_f32` instruction.
pub fn translate_u32_truncate_saturate_f32(&mut self) -> Result<(), ModuleError> {
self.translate_conversion(ValueType::F32, ValueType::I32, Instruction::I32TruncSatF32U)
}

/// Translate a Wasm `i32.truncate_sat_f64` instruction.
pub fn translate_i32_truncate_saturate_f64(&mut self) -> Result<(), ModuleError> {
self.translate_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncSatF64S)
}

/// Translate a Wasm `u32.truncate_sat_f64` instruction.
pub fn translate_u32_truncate_saturate_f64(&mut self) -> Result<(), ModuleError> {
self.translate_conversion(ValueType::F64, ValueType::I32, Instruction::I32TruncSatF64U)
}

/// Translate a Wasm `i64.truncate_sat_f32` instruction.
pub fn translate_i64_truncate_saturate_f32(&mut self) -> Result<(), ModuleError> {
self.translate_conversion(ValueType::F32, ValueType::I64, Instruction::I64TruncSatF32S)
}

/// Translate a Wasm `u64.truncate_sat_f32` instruction.
pub fn translate_u64_truncate_saturate_f32(&mut self) -> Result<(), ModuleError> {
self.translate_conversion(ValueType::F32, ValueType::I32, Instruction::I64TruncSatF32U)
}

/// Translate a Wasm `i64.truncate_sat_f64` instruction.
pub fn translate_i64_truncate_saturate_f64(&mut self) -> Result<(), ModuleError> {
self.translate_conversion(ValueType::F64, ValueType::I64, Instruction::I64TruncSatF64S)
}

/// Translate a Wasm `u64.truncate_sat_f64` instruction.
pub fn translate_u64_truncate_saturate_f64(&mut self) -> Result<(), ModuleError> {
self.translate_conversion(ValueType::F64, ValueType::I32, Instruction::I64TruncSatF64U)
}

/// Translate a Wasm `i32.extend_8s` instruction.
pub fn translate_i32_sign_extend8(&mut self) -> Result<(), ModuleError> {
self.translate_unary_operation(ValueType::I32, Instruction::I32Extend8S)
Expand Down
18 changes: 9 additions & 9 deletions wasmi_v1/src/module/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,20 +302,20 @@ impl<'engine, 'parser> FunctionTranslator<'engine, 'parser> {
Operator::I64ReinterpretF64 => self.translate_i64_reinterpret_f64(),
Operator::F32ReinterpretI32 => self.translate_f32_reinterpret_i32(),
Operator::F64ReinterpretI64 => self.translate_f64_reinterpret_i64(),
Operator::I32TruncSatF32S => self.translate_i32_truncate_saturate_f32(),
Operator::I32TruncSatF32U => self.translate_u32_truncate_saturate_f32(),
Operator::I32TruncSatF64S => self.translate_i32_truncate_saturate_f64(),
Operator::I32TruncSatF64U => self.translate_u32_truncate_saturate_f64(),
Operator::I64TruncSatF32S => self.translate_i64_truncate_saturate_f32(),
Operator::I64TruncSatF32U => self.translate_u64_truncate_saturate_f32(),
Operator::I64TruncSatF64S => self.translate_i64_truncate_saturate_f64(),
Operator::I64TruncSatF64U => self.translate_u64_truncate_saturate_f64(),
Operator::I32Extend8S => self.translate_i32_sign_extend8(),
Operator::I32Extend16S => self.translate_i32_sign_extend16(),
Operator::I64Extend8S => self.translate_i64_sign_extend8(),
Operator::I64Extend16S => self.translate_i64_sign_extend16(),
Operator::I64Extend32S => self.translate_i64_sign_extend32(),
Operator::I32TruncSatF32S
| Operator::I32TruncSatF32U
| Operator::I32TruncSatF64S
| Operator::I32TruncSatF64U
| Operator::I64TruncSatF32S
| Operator::I64TruncSatF32U
| Operator::I64TruncSatF64S
| Operator::I64TruncSatF64U
| Operator::MemoryInit { .. }
Operator::MemoryInit { .. }
| Operator::DataDrop { .. }
| Operator::MemoryCopy { .. }
| Operator::MemoryFill { .. }
Expand Down
16 changes: 16 additions & 0 deletions wasmi_v1/src/module/compile/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,22 @@ impl<'engine, 'parser> FunctionTranslator<'engine, 'parser> {
fn translate_f32_reinterpret_i32();
/// Translate a Wasm `f64_reinterpret_i64` instruction.
fn translate_f64_reinterpret_i64();
/// Translate a Wasm `i32.truncate_saturate_f32` instruction.
fn translate_i32_truncate_saturate_f32();
/// Translate a Wasm `u32.truncate_saturate_f32` instruction.
fn translate_u32_truncate_saturate_f32();
/// Translate a Wasm `i32.truncate_saturate_f64` instruction.
fn translate_i32_truncate_saturate_f64();
/// Translate a Wasm `u32.truncate_saturate_f64` instruction.
fn translate_u32_truncate_saturate_f64();
/// Translate a Wasm `i64.truncate_saturate_f32` instruction.
fn translate_i64_truncate_saturate_f32();
/// Translate a Wasm `u64.truncate_saturate_f32` instruction.
fn translate_u64_truncate_saturate_f32();
/// Translate a Wasm `i64.truncate_saturate_f64` instruction.
fn translate_i64_truncate_saturate_f64();
/// Translate a Wasm `u64.truncate_saturate_f64` instruction.
fn translate_u64_truncate_saturate_f64();
/// Translate a Wasm `i32.extend_i8` instruction.
fn translate_i32_sign_extend8();
/// Translate a Wasm `i32.extend_i16` instruction.
Expand Down