makepad/libs/relax/backend/tests/fminmax_nan.rs

53 lines
2.7 KiB
Rust

//! `f32/f64::min/max` return the other operand when one is NaN. The AVX lowering (min/max, an
//! unordered compare of the second operand and a blend) must give the bits of the SSE lowering
//! (min/max, compare, and/andn/or) for every pair, NaNs of both signs and payloads, zeros and infinities.
#![cfg(target_arch = "x86_64")]
use relax_backend::{exec::Code, rir::*, x64};
fn build(op: FOp, w: bool, no_avx: bool) -> Code {
let cls = if w { Cls::F64 } else { Cls::F32 };
let mut f = Func::new("fminmax".into(), 0);
let (a, b, d) = (f.vreg(cls), f.vreg(cls), f.vreg(cls));
f.params = vec![a, b];
f.rets = vec![cls];
f.block();
f.blocks[0].insts.push(Inst::FBin(op, w, d, a, b));
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;
let env = x64::Env { slots: SlotsPtr::NONE, tls_key: 0, tls_slow: 0, resume: 0, opt_regs: true, syms: 0, sym_class: 0, near: false };
let c = x64::compile(&options, &f, &env).unwrap();
Code::new(&c.code).unwrap()
}
#[test]
fn avx_min_max_match_the_sse_lowering() {
let f64s = [0.0, -0.0, 1.0, -1.0, 2.5, f64::INFINITY, f64::NEG_INFINITY, f64::NAN, -f64::NAN, f64::from_bits(0x7ff0000000000001), f64::MAX, f64::MIN_POSITIVE, f64::from_bits(1)];
let f32s: Vec<f32> = f64s.iter().map(|x| *x as f32).chain([f32::from_bits(0x7f800001), f32::from_bits(0xffc12345)]).collect();
for op in [FOp::Min, FOp::Max] {
let (a64, s64) = (build(op, true, false), build(op, true, true));
let (ga, gs): (extern "sysv64" fn(f64, f64) -> f64, extern "sysv64" fn(f64, f64) -> f64) = unsafe { (std::mem::transmute(a64.ptr()), std::mem::transmute(s64.ptr())) };
for &x in &f64s {
for &y in &f64s {
let (r, s) = (ga(x, y), gs(x, y));
assert!(r.to_bits() == s.to_bits(), "f64 {op:?}({x:e}, {y:e}): avx {:#x} sse {:#x}", r.to_bits(), s.to_bits());
if x.is_nan() && !y.is_nan() {
assert_eq!(r.to_bits(), y.to_bits());
}
if y.is_nan() && !x.is_nan() {
assert_eq!(r.to_bits(), x.to_bits());
}
}
}
let (a32, s32) = (build(op, false, false), build(op, false, true));
let (ga, gs): (extern "sysv64" fn(f32, f32) -> f32, extern "sysv64" fn(f32, f32) -> f32) = unsafe { (std::mem::transmute(a32.ptr()), std::mem::transmute(s32.ptr())) };
for &x in &f32s {
for &y in &f32s {
let (r, s) = (ga(x, y), gs(x, y));
assert!(r.to_bits() == s.to_bits(), "f32 {op:?}({x:e}, {y:e}): avx {:#x} sse {:#x}", r.to_bits(), s.to_bits());
}
}
}
}