Skip to content

Commit

Permalink
pulley: Implement float<->int conversions
Browse files Browse the repository at this point in the history
Gets the `conversions.wast` test running along with a few other misc
ones.

cc bytecodealliance#9783
  • Loading branch information
alexcrichton committed Dec 12, 2024
1 parent c41ce77 commit b585f9d
Show file tree
Hide file tree
Showing 5 changed files with 329 additions and 3 deletions.
88 changes: 88 additions & 0 deletions cranelift/codegen/src/isa/pulley_shared/lower.isle
Original file line number Diff line number Diff line change
Expand Up @@ -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))
1 change: 1 addition & 0 deletions crates/wasmtime/src/runtime/vm/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
3 changes: 0 additions & 3 deletions crates/wast-util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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)) {
Expand Down
184 changes: 184 additions & 0 deletions pulley/src/interp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,7 @@ mod done {
pub enum TrapKind {
DivideByZero,
IntegerOverflow,
BadConversionToInteger,
}

impl MachineState {
Expand Down Expand Up @@ -851,6 +852,17 @@ impl Interpreter<'_> {
.byte_offset(offset as isize)
.write_unaligned(val)
}

fn check_xnn_from_fnn<I: Encode>(&mut self, val: f64, lo: f64, hi: f64) -> ControlFlow<Done> {
if val != val {
return self.done_trap_kind::<I>(Some(TrapKind::BadConversionToInteger));
}
let val = val.trunc();
if val <= lo || val >= hi {
return self.done_trap_kind::<I>(Some(TrapKind::IntegerOverflow));
}
ControlFlow::Continue(())
}
}

#[test]
Expand Down Expand Up @@ -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<Done> {
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<Done> {
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<Done> {
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<Done> {
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<Done> {
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<Done> {
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<Done> {
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<Done> {
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<Done> {
let a = self.state[src].get_f32();
self.check_xnn_from_fnn::<crate::X32FromF32S>(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<Done> {
let a = self.state[src].get_f32();
self.check_xnn_from_fnn::<crate::X32FromF32U>(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<Done> {
let a = self.state[src].get_f32();
self.check_xnn_from_fnn::<crate::X64FromF32S>(
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<Done> {
let a = self.state[src].get_f32();
self.check_xnn_from_fnn::<crate::X64FromF32U>(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<Done> {
let a = self.state[src].get_f64();
self.check_xnn_from_fnn::<crate::X32FromF64S>(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<Done> {
let a = self.state[src].get_f64();
self.check_xnn_from_fnn::<crate::X32FromF64U>(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<Done> {
let a = self.state[src].get_f64();
self.check_xnn_from_fnn::<crate::X64FromF64S>(
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<Done> {
let a = self.state[src].get_f64();
self.check_xnn_from_fnn::<crate::X64FromF64U>(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<Done> {
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<Done> {
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<Done> {
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<Done> {
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<Done> {
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<Done> {
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<Done> {
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<Done> {
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<Done> {
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<Done> {
let a = self.state[src].get_f32();
self.state[dst].set_f64(a.into());
ControlFlow::Continue(())
}
}

impl ExtendedOpVisitor for Interpreter<'_> {
Expand Down
56 changes: 56 additions & 0 deletions pulley/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}
};
}
Expand Down

0 comments on commit b585f9d

Please sign in to comment.