Skip to content

Commit 408e2fe

Browse files
committed
cleanup FlushSubnormals implementation
1 parent 7d497cc commit 408e2fe

1 file changed

Lines changed: 34 additions & 70 deletions

File tree

crates/test_helpers/src/subnormals.rs

Lines changed: 34 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,81 +7,45 @@ pub trait FlushSubnormals: Sized {
77
impl<T> FlushSubnormals for *const T {}
88
impl<T> FlushSubnormals for *mut T {}
99

10-
macro_rules! impl_float {
11-
{ $($ty:ty),* } => {
12-
$(
13-
impl FlushSubnormals for $ty {
14-
fn flush(self) -> Self {
15-
let is_f32 = size_of::<Self>() == 4;
16-
let ppc_flush = is_f32 && cfg!(all(
17-
any(target_arch = "powerpc", all(target_arch = "powerpc64", target_endian = "big")),
18-
target_feature = "altivec",
19-
not(target_feature = "vsx"),
20-
));
21-
let arm_flush = is_f32 && cfg!(all(target_arch = "arm", target_feature = "neon"));
22-
let flush = ppc_flush || arm_flush;
23-
if flush && self.is_subnormal() {
24-
<$ty>::copysign(0., self)
25-
} else {
26-
self
27-
}
28-
}
10+
impl FlushSubnormals for i8 {}
11+
impl FlushSubnormals for i16 {}
12+
impl FlushSubnormals for i32 {}
13+
impl FlushSubnormals for i64 {}
14+
impl FlushSubnormals for isize {}
15+
16+
impl FlushSubnormals for u8 {}
17+
impl FlushSubnormals for u16 {}
18+
impl FlushSubnormals for u32 {}
19+
impl FlushSubnormals for u64 {}
20+
impl FlushSubnormals for usize {}
21+
22+
impl FlushSubnormals for f16 {}
23+
impl FlushSubnormals for f64 {}
24+
25+
impl FlushSubnormals for f32 {
26+
fn flush(self) -> Self {
27+
// Correct for non-IEEE behavior for f32 on arm and powerpc.
28+
let ppc_flush = cfg!(all(
29+
any(
30+
target_arch = "powerpc",
31+
all(target_arch = "powerpc64", target_endian = "big")
32+
),
33+
target_feature = "altivec",
34+
not(target_feature = "vsx"),
35+
));
36+
let arm_flush = cfg!(all(target_arch = "arm", target_feature = "neon"));
37+
let flush = ppc_flush || arm_flush;
38+
39+
if flush && self.is_subnormal() {
40+
f32::copysign(0.0, self)
41+
} else {
42+
self
2943
}
30-
)*
31-
}
32-
}
33-
34-
macro_rules! impl_else {
35-
{ $($ty:ty),* } => {
36-
$(
37-
impl FlushSubnormals for $ty {}
38-
)*
3944
}
4045
}
4146

42-
impl_float! { f16, f32, f64 }
43-
impl_else! { i8, i16, i32, i64, isize, u8, u16, u32, u64, usize }
44-
45-
/// AltiVec should flush subnormal inputs to zero, but QEMU seems to only flush outputs.
47+
/// NOTE: altivec had a subnormal flushing bug in older QEMU versions.
4648
/// https://gitlab.com/qemu-project/qemu/-/issues/1779
47-
#[cfg(all(
48-
any(target_arch = "powerpc", target_arch = "powerpc64"),
49-
target_feature = "altivec"
50-
))]
51-
fn in_buggy_qemu() -> bool {
52-
use std::sync::OnceLock;
53-
static BUGGY: OnceLock<bool> = OnceLock::new();
54-
55-
fn add(x: f32, y: f32) -> f32 {
56-
#[cfg(target_arch = "powerpc")]
57-
use core::arch::powerpc::*;
58-
#[cfg(target_arch = "powerpc64")]
59-
use core::arch::powerpc64::*;
60-
61-
let array: [f32; 4] =
62-
unsafe { core::mem::transmute(vec_add(vec_splats(x), vec_splats(y))) };
63-
array[0]
64-
}
65-
66-
*BUGGY.get_or_init(|| add(-1.0857398e-38, 0.).is_sign_negative())
67-
}
68-
69-
#[cfg(all(
70-
any(target_arch = "powerpc", target_arch = "powerpc64"),
71-
target_feature = "altivec"
72-
))]
73-
pub fn flush_in<T: FlushSubnormals>(x: T) -> T {
74-
if in_buggy_qemu() {
75-
x
76-
} else {
77-
x.flush()
78-
}
79-
}
80-
81-
#[cfg(not(all(
82-
any(target_arch = "powerpc", target_arch = "powerpc64"),
83-
target_feature = "altivec"
84-
)))]
8549
pub fn flush_in<T: FlushSubnormals>(x: T) -> T {
8650
x.flush()
8751
}

0 commit comments

Comments
 (0)