makepad/libs/relax/compiler/tests/fp_env.rs
Admin b22a714840 makepad run: the Rust JIT (libs/relax) and makepad-remote
The JIT that runs makepad apps from source: lexer, front end, type checker,
lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and
warm/cold caches keyed by compiler stamp, provided crates bound late,
startup images, live patching. User-facing name is `makepad run` (binary
`makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal
library name. `makepad check` reports rustc-style diagnostics with rustc
error codes and checks a whole workspace (`--workspace`). Runs on macOS,
Linux, Windows and Android. makepad-remote: one tool schema for apps and the
WM (app/1): grabs, captures, traces, frame profiles and tweaks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:26:21 +02:00

87 lines
2.7 KiB
Rust

// The floating-point control register as a builtin (x86-64 MXCSR via _mm_getcsr/_mm_setcsr,
// AArch64 FPCR via core::arch::aarch64::{get_fpcr, set_fpcr} under Relax): round trips, and
// flush-to-zero / denormals-are-zero change results (subnormal products become 0) inside the
// region they are set in and only there. Output = rustc --test.
use std::hint::black_box;
#[cfg(target_arch = "x86_64")]
mod env {
#[allow(deprecated)]
pub fn get() -> u64 {
unsafe { core::arch::x86_64::_mm_getcsr() as u64 }
}
#[allow(deprecated)]
pub fn set(x: u64) {
unsafe { core::arch::x86_64::_mm_setcsr(x as u32) }
}
/// FTZ (bit 15) + DAZ (bit 6)
pub const FLUSH: u64 = 0x8040;
}
#[cfg(target_arch = "aarch64")]
mod env {
#[cfg(relax)]
pub fn get() -> u64 {
unsafe { core::arch::aarch64::get_fpcr() }
}
#[cfg(relax)]
pub fn set(x: u64) {
unsafe { core::arch::aarch64::set_fpcr(x) }
}
#[cfg(not(relax))]
pub fn get() -> u64 {
let r: u64;
unsafe { std::arch::asm!("mrs {0}, fpcr", out(reg) r) };
r
}
#[cfg(not(relax))]
pub fn set(x: u64) {
unsafe { std::arch::asm!("msr fpcr, {0}", in(reg) x) };
}
/// FZ (bit 24)
pub const FLUSH: u64 = 1 << 24;
}
#[inline(never)]
fn products(a: &[f32], k: f32, out: &mut [f32]) {
for i in 0..a.len() {
out[i] = a[i] * k;
}
}
#[inline(never)]
fn sum64(a: &[f64], k: f64) -> f64 {
let mut s = 0.0;
for x in a {
s += x * k;
}
s
}
#[test]
fn fp_env() {
let base = env::get();
let a: Vec<f32> = (0..64).map(|i| f32::MIN_POSITIVE * (1.0 + i as f32 / 64.0)).collect();
let d: Vec<f64> = (0..64).map(|i| f64::MIN_POSITIVE * (1.0 + i as f64 / 64.0)).collect();
let mut h = 0u64;
for round in 0..300 {
let mut out = vec![0f32; a.len()];
products(black_box(&a), black_box(0.25), &mut out);
let s0 = sum64(black_box(&d), black_box(0.125));
h = h.wrapping_mul(31).wrapping_add(out.iter().map(|x| x.to_bits() as u64).sum::<u64>() ^ s0.to_bits());
env::set(base | env::FLUSH);
let on = env::get();
let mut out2 = vec![0f32; a.len()];
products(black_box(&a), black_box(0.25), &mut out2);
let s1 = sum64(black_box(&d), black_box(0.125));
black_box(&out2);
black_box(s1);
env::set(base);
let off = env::get();
h = h.wrapping_mul(31).wrapping_add(out2.iter().map(|x| x.to_bits() as u64).sum::<u64>() ^ s1.to_bits());
if round == 0 {
println!("| set {} restored {} flushed {}", on & env::FLUSH == env::FLUSH, off == base, out2.iter().all(|x| *x == 0.0) && s1 == 0.0);
}
}
println!("| {:016x}", h);
}