71 lines
3.2 KiB
Rust
71 lines
3.2 KiB
Rust
//! Saturating float -> int casts (`Conv::FToInt`, Rust `as`): NaN -> 0, out of range clamps, the
|
|
//! integer kept extended to 64 bits by its type. The x64 targets whose bounds the float type holds
|
|
//! exactly (f32 -> 8/16 bits, f64 -> 8/16 bits and u32) clamp with maxss / minss and convert once;
|
|
//! every target must give the bits of Rust's cast for NaNs, infinities, zeros, the bounds and their
|
|
//! neighbours, in the AVX and the SSE forms.
|
|
#![cfg(target_arch = "x86_64")]
|
|
use relax_backend::{exec::Code, rir::*, x64};
|
|
|
|
fn build(from64: bool, it: IntTy, no_avx: bool, opt_regs: bool) -> Code {
|
|
let cls = if from64 { Cls::F64 } else { Cls::F32 };
|
|
let mut f = Func::new("ftoint".into(), 0);
|
|
let (a, d) = (f.vreg(cls), f.vreg(Cls::I));
|
|
f.params = vec![a];
|
|
f.rets = vec![Cls::I];
|
|
f.block();
|
|
f.blocks[0].insts.push(Inst::Conv(Conv::FToInt(from64, it), d, a));
|
|
f.blocks[0].pos.push(0);
|
|
f.blocks[0].term = Term::Ret(vec![d]);
|
|
f.verify().unwrap();
|
|
let mut options = relax_session::Options::default();
|
|
options.no_avx = no_avx;
|
|
options.cpu.avx = !no_avx && std::arch::is_x86_feature_detected!("avx");
|
|
let env = x64::Env { slots: SlotsPtr::NONE, tls_key: 0, tls_slow: 0, resume: 0, opt_regs, syms: 0, sym_class: 0, near: false };
|
|
let c = x64::compile(&options, &f, &env).unwrap();
|
|
Code::new(&c.code).unwrap()
|
|
}
|
|
|
|
fn reference(x: f64, it: IntTy) -> u64 {
|
|
match (it.signed, it.bits) {
|
|
(false, 8) => (x as u8) as u64,
|
|
(true, 8) => (x as i8) as i64 as u64,
|
|
(false, 16) => (x as u16) as u64,
|
|
(true, 16) => (x as i16) as i64 as u64,
|
|
(false, 32) => (x as u32) as u64,
|
|
(true, 32) => (x as i32) as i64 as u64,
|
|
(false, 64) => x as u64,
|
|
_ => (x as i64) as u64,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn casts_match_rust_as() {
|
|
let mut xs: Vec<f64> = vec![0.0, -0.0, 0.4, 0.5, 0.99, 1.0, -0.5, -0.99, -1.0, -1.5, 2.5, 1e30, -1e30, f64::INFINITY, f64::NEG_INFINITY, f64::NAN, -f64::NAN, f64::MAX, f64::MIN_POSITIVE];
|
|
for b in [8, 16, 32, 64] {
|
|
for e in [(2f64).powi(b - 1), (2f64).powi(b)] {
|
|
for d in [-1.5, -1.0, -0.5, 0.0, 0.5, 1.0] {
|
|
xs.push(e + d);
|
|
xs.push(-e + d);
|
|
}
|
|
}
|
|
}
|
|
for (from64, no_avx, opt_regs) in [(false, false, true), (true, false, true), (false, true, true), (true, true, true), (false, false, false), (true, false, false)] {
|
|
for bits in [8u8, 16, 32, 64] {
|
|
for signed in [false, true] {
|
|
let it = IntTy { bits, signed };
|
|
let code = build(from64, it, no_avx, opt_regs);
|
|
for &x in &xs {
|
|
let (got, want) = if from64 {
|
|
let g: extern "sysv64" fn(f64) -> u64 = unsafe { std::mem::transmute(code.ptr()) };
|
|
(g(x), reference(x, it))
|
|
} else {
|
|
let g: extern "sysv64" fn(f32) -> u64 = unsafe { std::mem::transmute(code.ptr()) };
|
|
let x = x as f32;
|
|
(g(x), reference(x as f64, it))
|
|
};
|
|
assert_eq!(got, want, "{} -> {}{} (no_avx {}): x = {:e}", if from64 { "f64" } else { "f32" }, if signed { "i" } else { "u" }, bits, no_avx, x);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|