From b585f9df316db5a2fc7b583ac1f179c44662982b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 Dec 2024 08:09:32 -0800 Subject: [PATCH] pulley: Implement float<->int conversions Gets the `conversions.wast` test running along with a few other misc ones. cc #9783 --- .../codegen/src/isa/pulley_shared/lower.isle | 88 +++++++++ crates/wasmtime/src/runtime/vm/interpreter.rs | 1 + crates/wast-util/src/lib.rs | 3 - pulley/src/interp.rs | 184 ++++++++++++++++++ pulley/src/lib.rs | 56 ++++++ 5 files changed, 329 insertions(+), 3 deletions(-) diff --git a/cranelift/codegen/src/isa/pulley_shared/lower.isle b/cranelift/codegen/src/isa/pulley_shared/lower.isle index e4eecca589bc..35f927ae4cb6 100644 --- a/cranelift/codegen/src/isa/pulley_shared/lower.isle +++ b/cranelift/codegen/src/isa/pulley_shared/lower.isle @@ -502,3 +502,91 @@ (rule (lower (has_type $I64 (bitcast _flags val @ (value_type $F64)))) (pulley_bitcast_int_from_float_64 val)) + +;;;; Rules for `fcvt_to_{u,s}int` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule (lower (has_type $I32 (fcvt_to_uint val @ (value_type $F32)))) + (pulley_x32_from_f32_u val)) + +(rule (lower (has_type $I32 (fcvt_to_uint val @ (value_type $F64)))) + (pulley_x32_from_f64_u val)) + +(rule (lower (has_type $I64 (fcvt_to_uint val @ (value_type $F32)))) + (pulley_x64_from_f32_u val)) + +(rule (lower (has_type $I64 (fcvt_to_uint val @ (value_type $F64)))) + (pulley_x64_from_f64_u val)) + +(rule (lower (has_type $I32 (fcvt_to_sint val @ (value_type $F32)))) + (pulley_x32_from_f32_s val)) + +(rule (lower (has_type $I32 (fcvt_to_sint val @ (value_type $F64)))) + (pulley_x32_from_f64_s val)) + +(rule (lower (has_type $I64 (fcvt_to_sint val @ (value_type $F32)))) + (pulley_x64_from_f32_s val)) + +(rule (lower (has_type $I64 (fcvt_to_sint val @ (value_type $F64)))) + (pulley_x64_from_f64_s val)) + +;;;; Rules for `fcvt_from_{u,s}int` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule (lower (has_type $F32 (fcvt_from_uint val @ (value_type $I32)))) + (pulley_f32_from_x32_u val)) + +(rule (lower (has_type $F32 (fcvt_from_uint val @ (value_type $I64)))) + (pulley_f32_from_x64_u val)) + +(rule (lower (has_type $F64 (fcvt_from_uint val @ (value_type $I32)))) + (pulley_f64_from_x32_u val)) + +(rule (lower (has_type $F64 (fcvt_from_uint val @ (value_type $I64)))) + (pulley_f64_from_x64_u val)) + +(rule (lower (has_type $F32 (fcvt_from_sint val @ (value_type $I32)))) + (pulley_f32_from_x32_s val)) + +(rule (lower (has_type $F32 (fcvt_from_sint val @ (value_type $I64)))) + (pulley_f32_from_x64_s val)) + +(rule (lower (has_type $F64 (fcvt_from_sint val @ (value_type $I32)))) + (pulley_f64_from_x32_s val)) + +(rule (lower (has_type $F64 (fcvt_from_sint val @ (value_type $I64)))) + (pulley_f64_from_x64_s val)) + +;;;; Rules for `fcvt_to_{u,s}int_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule (lower (has_type $I32 (fcvt_to_uint_sat val @ (value_type $F32)))) + (pulley_x32_from_f32_u_sat val)) + +(rule (lower (has_type $I32 (fcvt_to_uint_sat val @ (value_type $F64)))) + (pulley_x32_from_f64_u_sat val)) + +(rule (lower (has_type $I64 (fcvt_to_uint_sat val @ (value_type $F32)))) + (pulley_x64_from_f32_u_sat val)) + +(rule (lower (has_type $I64 (fcvt_to_uint_sat val @ (value_type $F64)))) + (pulley_x64_from_f64_u_sat val)) + +(rule (lower (has_type $I32 (fcvt_to_sint_sat val @ (value_type $F32)))) + (pulley_x32_from_f32_s_sat val)) + +(rule (lower (has_type $I32 (fcvt_to_sint_sat val @ (value_type $F64)))) + (pulley_x32_from_f64_s_sat val)) + +(rule (lower (has_type $I64 (fcvt_to_sint_sat val @ (value_type $F32)))) + (pulley_x64_from_f32_s_sat val)) + +(rule (lower (has_type $I64 (fcvt_to_sint_sat val @ (value_type $F64)))) + (pulley_x64_from_f64_s_sat val)) + +;;;; Rules for `fdemote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule (lower (has_type $F32 (fdemote val @ (value_type $F64)))) + (pulley_f32_from_f64 val)) + +;;;; Rules for `fpromote` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule (lower (has_type $F64 (fpromote val @ (value_type $F32)))) + (pulley_f64_from_f32 val)) diff --git a/crates/wasmtime/src/runtime/vm/interpreter.rs b/crates/wasmtime/src/runtime/vm/interpreter.rs index dfb353ef13c1..a0ec7a8e196a 100644 --- a/crates/wasmtime/src/runtime/vm/interpreter.rs +++ b/crates/wasmtime/src/runtime/vm/interpreter.rs @@ -137,6 +137,7 @@ impl InterpreterRef<'_> { let trap = match kind { TrapKind::IntegerOverflow => Trap::IntegerOverflow, TrapKind::DivideByZero => Trap::IntegerDivisionByZero, + TrapKind::BadConversionToInteger => Trap::BadConversionToInteger, }; s.set_jit_trap(regs, None, trap); } diff --git a/crates/wast-util/src/lib.rs b/crates/wast-util/src/lib.rs index c4257d0586c8..0b94d96809bc 100644 --- a/crates/wast-util/src/lib.rs +++ b/crates/wast-util/src/lib.rs @@ -401,7 +401,6 @@ impl WastTest { "misc_testsuite/embenchen_primes.wast", "misc_testsuite/float-round-doesnt-load-too-much.wast", "misc_testsuite/int-to-float-splat.wast", - "misc_testsuite/issue4840.wast", "misc_testsuite/issue4890.wast", "misc_testsuite/issue6562.wast", "misc_testsuite/memory-combos.wast", @@ -433,7 +432,6 @@ impl WastTest { "misc_testsuite/winch/_simd_store.wast", "spec_testsuite/call.wast", "spec_testsuite/call_indirect.wast", - "spec_testsuite/conversions.wast", "spec_testsuite/f32.wast", "spec_testsuite/f32_bitwise.wast", "spec_testsuite/f32_cmp.wast", @@ -518,7 +516,6 @@ impl WastTest { "spec_testsuite/simd_store64_lane.wast", "spec_testsuite/simd_store8_lane.wast", "spec_testsuite/switch.wast", - "spec_testsuite/traps.wast", ]; if unsupported.iter().any(|part| self.path.ends_with(part)) { diff --git a/pulley/src/interp.rs b/pulley/src/interp.rs index 3e76373a951f..34b787cbaca8 100644 --- a/pulley/src/interp.rs +++ b/pulley/src/interp.rs @@ -706,6 +706,7 @@ mod done { pub enum TrapKind { DivideByZero, IntegerOverflow, + BadConversionToInteger, } impl MachineState { @@ -851,6 +852,17 @@ impl Interpreter<'_> { .byte_offset(offset as isize) .write_unaligned(val) } + + fn check_xnn_from_fnn(&mut self, val: f64, lo: f64, hi: f64) -> ControlFlow { + if val != val { + return self.done_trap_kind::(Some(TrapKind::BadConversionToInteger)); + } + let val = val.trunc(); + if val <= lo || val >= hi { + return self.done_trap_kind::(Some(TrapKind::IntegerOverflow)); + } + ControlFlow::Continue(()) + } } #[test] @@ -1947,6 +1959,178 @@ impl OpVisitor for Interpreter<'_> { self.state[dst].set_f64(result); ControlFlow::Continue(()) } + + fn f32_from_x32_s(&mut self, dst: FReg, src: XReg) -> ControlFlow { + let a = self.state[src].get_i32(); + self.state[dst].set_f32(a as f32); + ControlFlow::Continue(()) + } + + fn f32_from_x32_u(&mut self, dst: FReg, src: XReg) -> ControlFlow { + let a = self.state[src].get_u32(); + self.state[dst].set_f32(a as f32); + ControlFlow::Continue(()) + } + + fn f32_from_x64_s(&mut self, dst: FReg, src: XReg) -> ControlFlow { + let a = self.state[src].get_i64(); + self.state[dst].set_f32(a as f32); + ControlFlow::Continue(()) + } + + fn f32_from_x64_u(&mut self, dst: FReg, src: XReg) -> ControlFlow { + let a = self.state[src].get_u64(); + self.state[dst].set_f32(a as f32); + ControlFlow::Continue(()) + } + + fn f64_from_x32_s(&mut self, dst: FReg, src: XReg) -> ControlFlow { + let a = self.state[src].get_i32(); + self.state[dst].set_f64(a as f64); + ControlFlow::Continue(()) + } + + fn f64_from_x32_u(&mut self, dst: FReg, src: XReg) -> ControlFlow { + let a = self.state[src].get_u32(); + self.state[dst].set_f64(a as f64); + ControlFlow::Continue(()) + } + + fn f64_from_x64_s(&mut self, dst: FReg, src: XReg) -> ControlFlow { + let a = self.state[src].get_i64(); + self.state[dst].set_f64(a as f64); + ControlFlow::Continue(()) + } + + fn f64_from_x64_u(&mut self, dst: FReg, src: XReg) -> ControlFlow { + let a = self.state[src].get_u64(); + self.state[dst].set_f64(a as f64); + ControlFlow::Continue(()) + } + + fn x32_from_f32_s(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f32(); + self.check_xnn_from_fnn::(a.into(), -2147483649.0, 2147483648.0)?; + self.state[dst].set_i32(a as i32); + ControlFlow::Continue(()) + } + + fn x32_from_f32_u(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f32(); + self.check_xnn_from_fnn::(a.into(), -1.0, 4294967296.0)?; + self.state[dst].set_u32(a as u32); + ControlFlow::Continue(()) + } + + fn x64_from_f32_s(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f32(); + self.check_xnn_from_fnn::( + a.into(), + -9223372036854777856.0, + 9223372036854775808.0, + )?; + self.state[dst].set_i64(a as i64); + ControlFlow::Continue(()) + } + + fn x64_from_f32_u(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f32(); + self.check_xnn_from_fnn::(a.into(), -1.0, 18446744073709551616.0)?; + self.state[dst].set_u64(a as u64); + ControlFlow::Continue(()) + } + + fn x32_from_f64_s(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f64(); + self.check_xnn_from_fnn::(a, -2147483649.0, 2147483648.0)?; + self.state[dst].set_i32(a as i32); + ControlFlow::Continue(()) + } + + fn x32_from_f64_u(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f64(); + self.check_xnn_from_fnn::(a, -1.0, 4294967296.0)?; + self.state[dst].set_u32(a as u32); + ControlFlow::Continue(()) + } + + fn x64_from_f64_s(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f64(); + self.check_xnn_from_fnn::( + a, + -9223372036854777856.0, + 9223372036854775808.0, + )?; + self.state[dst].set_i64(a as i64); + ControlFlow::Continue(()) + } + + fn x64_from_f64_u(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f64(); + self.check_xnn_from_fnn::(a, -1.0, 18446744073709551616.0)?; + self.state[dst].set_u64(a as u64); + ControlFlow::Continue(()) + } + + fn x32_from_f32_s_sat(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f32(); + self.state[dst].set_i32(a as i32); + ControlFlow::Continue(()) + } + + fn x32_from_f32_u_sat(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f32(); + self.state[dst].set_u32(a as u32); + ControlFlow::Continue(()) + } + + fn x64_from_f32_s_sat(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f32(); + self.state[dst].set_i64(a as i64); + ControlFlow::Continue(()) + } + + fn x64_from_f32_u_sat(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f32(); + self.state[dst].set_u64(a as u64); + ControlFlow::Continue(()) + } + + fn x32_from_f64_s_sat(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f64(); + self.state[dst].set_i32(a as i32); + ControlFlow::Continue(()) + } + + fn x32_from_f64_u_sat(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f64(); + self.state[dst].set_u32(a as u32); + ControlFlow::Continue(()) + } + + fn x64_from_f64_s_sat(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f64(); + self.state[dst].set_i64(a as i64); + ControlFlow::Continue(()) + } + + fn x64_from_f64_u_sat(&mut self, dst: XReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f64(); + self.state[dst].set_u64(a as u64); + ControlFlow::Continue(()) + } + + fn f32_from_f64(&mut self, dst: FReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f64(); + self.state[dst].set_f32(a as f32); + ControlFlow::Continue(()) + } + + fn f64_from_f32(&mut self, dst: FReg, src: FReg) -> ControlFlow { + let a = self.state[src].get_f32(); + self.state[dst].set_f64(a.into()); + ControlFlow::Continue(()) + } } impl ExtendedOpVisitor for Interpreter<'_> { diff --git a/pulley/src/lib.rs b/pulley/src/lib.rs index bffb05c3484a..2781c793f3ed 100644 --- a/pulley/src/lib.rs +++ b/pulley/src/lib.rs @@ -421,6 +421,62 @@ macro_rules! for_each_op { fselect32 = FSelect32 { dst: FReg, cond: XReg, if_nonzero: FReg, if_zero: FReg }; /// `dst = low32(cond) ? if_nonzero : if_zero` fselect64 = FSelect64 { dst: FReg, cond: XReg, if_nonzero: FReg, if_zero: FReg }; + + /// `low32(dst) = checked_f32_from_signed(low32(src))` + f32_from_x32_s = F32FromX32S { dst: FReg, src: XReg }; + /// `low32(dst) = checked_f32_from_unsigned(low32(src))` + f32_from_x32_u = F32FromX32U { dst: FReg, src: XReg }; + /// `low32(dst) = checked_f32_from_signed(src)` + f32_from_x64_s = F32FromX64S { dst: FReg, src: XReg }; + /// `low32(dst) = checked_f32_from_unsigned(src)` + f32_from_x64_u = F32FromX64U { dst: FReg, src: XReg }; + /// `dst = checked_f64_from_signed(low32(src))` + f64_from_x32_s = F64FromX32S { dst: FReg, src: XReg }; + /// `dst = checked_f64_from_unsigned(low32(src))` + f64_from_x32_u = F64FromX32U { dst: FReg, src: XReg }; + /// `dst = checked_f64_from_signed(src)` + f64_from_x64_s = F64FromX64S { dst: FReg, src: XReg }; + /// `dst = checked_f64_from_unsigned(src)` + f64_from_x64_u = F64FromX64U { dst: FReg, src: XReg }; + + /// `low32(dst) = checked_signed_from_f32(low32(src))` + x32_from_f32_s = X32FromF32S { dst: XReg, src: FReg }; + /// `low32(dst) = checked_unsigned_from_f32(low32(src))` + x32_from_f32_u = X32FromF32U { dst: XReg, src: FReg }; + /// `low32(dst) = checked_signed_from_f64(src)` + x32_from_f64_s = X32FromF64S { dst: XReg, src: FReg }; + /// `low32(dst) = checked_unsigned_from_f64(src)` + x32_from_f64_u = X32FromF64U { dst: XReg, src: FReg }; + /// `dst = checked_signed_from_f32(low32(src))` + x64_from_f32_s = X64FromF32S { dst: XReg, src: FReg }; + /// `dst = checked_unsigned_from_f32(low32(src))` + x64_from_f32_u = X64FromF32U { dst: XReg, src: FReg }; + /// `dst = checked_signed_from_f64(src)` + x64_from_f64_s = X64FromF64S { dst: XReg, src: FReg }; + /// `dst = checked_unsigned_from_f64(src)` + x64_from_f64_u = X64FromF64U { dst: XReg, src: FReg }; + + /// `low32(dst) = saturating_signed_from_f32(low32(src))` + x32_from_f32_s_sat = X32FromF32SSat { dst: XReg, src: FReg }; + /// `low32(dst) = saturating_unsigned_from_f32(low32(src))` + x32_from_f32_u_sat = X32FromF32USat { dst: XReg, src: FReg }; + /// `low32(dst) = saturating_signed_from_f64(src)` + x32_from_f64_s_sat = X32FromF64SSat { dst: XReg, src: FReg }; + /// `low32(dst) = saturating_unsigned_from_f64(src)` + x32_from_f64_u_sat = X32FromF64USat { dst: XReg, src: FReg }; + /// `dst = saturating_signed_from_f32(low32(src))` + x64_from_f32_s_sat = X64FromF32SSat { dst: XReg, src: FReg }; + /// `dst = saturating_unsigned_from_f32(low32(src))` + x64_from_f32_u_sat = X64FromF32USat { dst: XReg, src: FReg }; + /// `dst = saturating_signed_from_f64(src)` + x64_from_f64_s_sat = X64FromF64SSat { dst: XReg, src: FReg }; + /// `dst = saturating_unsigned_from_f64(src)` + x64_from_f64_u_sat = X64FromF64USat { dst: XReg, src: FReg }; + + /// `low32(dst) = demote(src)` + f32_from_f64 = F32FromF64 { dst: FReg, src: FReg }; + /// `(st) = promote(low32(src))` + f64_from_f32 = F64FromF32 { dst: FReg, src: FReg }; } }; }