pub(crate) use imp::Guard as DenormalGuard;
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
mod imp {
#![allow(deprecated)]
#[cfg(target_arch = "x86")]
use core::arch::x86::{_mm_getcsr, _mm_setcsr};
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::{_mm_getcsr, _mm_setcsr};
const FTZ: u32 = 1 << 15;
const DAZ: u32 = 1 << 6;
pub(crate) struct Guard {
previous: u32,
}
impl Guard {
#[inline]
pub(crate) fn new() -> Self {
#[allow(deprecated)]
let previous = unsafe { _mm_getcsr() };
#[allow(deprecated)]
unsafe {
_mm_setcsr(previous | FTZ | DAZ);
}
Guard { previous }
}
}
impl Drop for Guard {
#[inline]
fn drop(&mut self) {
#[allow(deprecated)]
unsafe {
_mm_setcsr(self.previous);
}
}
}
}
#[cfg(target_arch = "aarch64")]
mod imp {
const FZ: u64 = 1 << 24;
pub(crate) struct Guard {
previous: u64,
}
impl Guard {
#[inline]
pub(crate) fn new() -> Self {
let previous: u64;
unsafe {
core::arch::asm!("mrs {}, fpcr", out(reg) previous, options(nomem, nostack));
core::arch::asm!("msr fpcr, {}", in(reg) previous | FZ, options(nomem, nostack));
}
Guard { previous }
}
}
impl Drop for Guard {
#[inline]
fn drop(&mut self) {
unsafe {
core::arch::asm!("msr fpcr, {}", in(reg) self.previous, options(nomem, nostack));
}
}
}
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))]
mod imp {
pub(crate) struct Guard;
impl Guard {
#[inline]
pub(crate) fn new() -> Self {
Guard
}
}
}
#[cfg(test)]
#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"))]
mod tests {
use super::*;
use std::hint::black_box;
fn make_denormal() -> f32 {
black_box(f32::MIN_POSITIVE) * black_box(0.01_f32)
}
#[test]
fn guard_flushes_denormals_to_zero() {
let without = make_denormal();
assert!(
without > 0.0 && without < f32::MIN_POSITIVE,
"expected a nonzero denormal without the guard, got {without:e}"
);
let with = {
let _g = DenormalGuard::new();
make_denormal()
};
assert_eq!(with, 0.0, "denormal was not flushed inside the guard");
let after = make_denormal();
assert!(
after > 0.0 && after < f32::MIN_POSITIVE,
"guard did not restore prior FPU state, got {after:e}"
);
}
}