Skip to content

Commit

Permalink
pulley: Implement float<->int conversions (#9804)
Browse files Browse the repository at this point in the history
* pulley: Implement float<->int conversions

Gets the `conversions.wast` test running along with a few other misc
ones.

cc #9783

* Fix pulley's no_std build

* One more conversion to a workspace dep
  • Loading branch information
alexcrichton authored Dec 12, 2024
1 parent ef9c954 commit 3ec924f
Show file tree
Hide file tree
Showing 11 changed files with 358 additions and 6 deletions.
1 change: 1 addition & 0 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 @@ -353,6 +353,7 @@ rustc-hash = "2.0.0"
libtest-mimic = "0.7.0"
semver = { version = "1.0.17", default-features = false }
ittapi = "0.4.0"
libm = "0.2.7"

# =============================================================================
#
Expand Down
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 @@ -495,3 +495,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))
2 changes: 1 addition & 1 deletion cranelift/interpreter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ smallvec = { workspace = true }
thiserror = { workspace = true }

[target.x86_64-pc-windows-gnu.dependencies]
libm = "0.2.4"
libm = { workspace = true }

[dev-dependencies]
cranelift-frontend = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/wasmtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ addr2line = { workspace = true, optional = true }
semver = { workspace = true, optional = true }
smallvec = { workspace = true, optional = true }
hashbrown = { workspace = true, features = ["ahash"] }
libm = "0.2.7"
libm = { workspace = true }
bitflags = { workspace = true }

[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
Expand Down
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
3 changes: 2 additions & 1 deletion pulley/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ arbitrary = { workspace = true, optional = true }
cranelift-bitset = { workspace = true }
log = { workspace = true }
sptr = { workspace = true }
libm = { workspace = true, optional = true }

[dev-dependencies]
env_logger = { workspace = true }
Expand All @@ -29,7 +30,7 @@ arbitrary = ["dep:arbitrary", "arbitrary/derive", "std", "cranelift-bitset/arbit
encode = []
decode = []
disas = ["decode"]
interp = ["decode", "encode"]
interp = ["decode", "encode", "dep:libm"]

[package.metadata.docs.rs]
all-features = true
Expand Down
189 changes: 189 additions & 0 deletions pulley/src/interp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ mod match_loop;
#[cfg(any(pulley_tail_calls, pulley_assume_llvm_makes_tail_calls))]
mod tail_loop;

#[cfg(not(feature = "std"))]
mod float_ext;
#[cfg(not(feature = "std"))]
use self::float_ext::FloatExt;

const DEFAULT_STACK_SIZE: usize = 1 << 20; // 1 MiB

/// A virtual machine for interpreting Pulley bytecode.
Expand Down Expand Up @@ -706,6 +711,7 @@ mod done {
pub enum TrapKind {
DivideByZero,
IntegerOverflow,
BadConversionToInteger,
}

impl MachineState {
Expand Down Expand Up @@ -851,6 +857,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 +1964,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
Loading

0 comments on commit 3ec924f

Please sign in to comment.