From e5419e44919449947e9b64f30d71c8a5c8600ad2 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Sun, 26 Jul 2026 21:22:07 +0900 Subject: [PATCH] relibc: implement _fenv POSIX functions (fenv.h) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace 11 unimplemented!() stubs in src/header/_fenv/mod.rs with real x86 MXCSR + x87 FPU control word implementations: feclearexcept, fegetexceptflag, feraiseexcept, fesetexceptflag, fetestexcept — exception flag management via stmxcsr/ldmxcsr fegetround, fesetround — rounding mode via MXCSR bits 13-14 fegetenv, feholdexcept, fesetenv, feupdateenv — full FP environment save/restore via stmxcsr + fnstcw / ldmxcsr + fldcw Constants corrected to match MXCSR bit positions: FE_INVALID=0x01, FE_DIVBYZERO=0x04, FE_OVERFLOW=0x08, FE_UNDERFLOW=0x10, FE_INEXACT=0x20, FE_ALL_EXCEPT=0x3D FE_TONEAREST=0, FE_DOWNWARD=1, FE_UPWARD=2, FE_TOWARDZERO=3 Types corrected to match x86-64 ABI: fexcept_t: u32 (MXCSR subset) fenv_t: struct { x87_cw: u16, _reserved: u16, mxcsr: u32 } Non-x86 architectures fall through to no-op implementations (returning 0 / FE_TONEAREST) guarded by cfg attributes. This is part of the Round 4 stub sweep. The remaining relibc unimplemented!() instances are mostly in commented-out code (functions awaiting locale_t support) or are complex POSIX functions requiring further implementation work. --- src/header/_fenv/mod.rs | 243 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 223 insertions(+), 20 deletions(-) diff --git a/src/header/_fenv/mod.rs b/src/header/_fenv/mod.rs index c09b2882a7..653322642a 100644 --- a/src/header/_fenv/mod.rs +++ b/src/header/_fenv/mod.rs @@ -1,85 +1,288 @@ //! `fenv.h` implementation. //! //! See . +//! +//! On x86/x86_64 the floating-point environment is split between the +//! x87 FPU control word (`fnstcw`/`fldcw`, 16-bit) and the SSE MXCSR +//! register (`stmxcsr`/`ldmxcsr`, 32-bit). The POSIX `FE_*` exception +//! constants are defined to match the MXCSR exception flag bits so +//! that `feclearexcept`/`fetestexcept` can operate via direct bitwise +//! AND/OR on a stored MXCSR value. +//! +//! Rounding mode uses MXCSR bits 13-14 (0=nearest, 1=down, 2=up, +//! 3=toward-zero), matching the POSIX `FE_TONEAREST`/`FE_DOWNWARD`/ +//! `FE_UPWARD`/`FE_TOWARDZERO` values directly. -use crate::platform::types::{c_int, c_ulonglong}; +use crate::platform::types::{c_int, c_uint}; + +// --- Exception flag constants (match MXCSR bit positions) --- + +pub const FE_INVALID: c_int = 0x01; +pub const FE_DIVBYZERO: c_int = 0x04; +pub const FE_OVERFLOW: c_int = 0x08; +pub const FE_UNDERFLOW: c_int = 0x10; +pub const FE_INEXACT: c_int = 0x20; +pub const FE_ALL_EXCEPT: c_int = + FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT; + +// --- Rounding mode constants (match MXCSR bits 13-14) --- -/// See . -pub const FE_ALL_EXCEPT: c_int = 0; -/// See . pub const FE_TONEAREST: c_int = 0; +pub const FE_DOWNWARD: c_int = 1; +pub const FE_UPWARD: c_int = 2; +pub const FE_TOWARDZERO: c_int = 3; -/// See . -pub type fexcept_t = c_ulonglong; +/// Stored FP exception flags. On x86 this is a subset of the MXCSR +/// register containing the exception flag bits (bits 0-5). +pub type fexcept_t = c_uint; -/// See . +/// Complete floating-point environment: x87 control word + SSE MXCSR. +/// The layout matches the structure used by `fegetenv`/`fesetenv` on +/// x86-64 Linux (glibc's `fenv_t` union stores the same two fields). #[repr(C)] pub struct fenv_t { - pub cw: c_ulonglong, + pub x87_cw: u16, + pub _reserved0: u16, + pub mxcsr: u32, +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn read_mxcsr() -> u32 { + let mut mxcsr: u32 = 0; + unsafe { + core::arch::asm!( + "stmxcsr [{addr}]", + addr = in(reg) core::ptr::addr_of_mut!(mxcsr), + options(nostack, preserves_flags), + ); + } + mxcsr +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn write_mxcsr(mxcsr: u32) { + unsafe { + core::arch::asm!( + "ldmxcsr [{addr}]", + addr = in(reg) core::ptr::addr_of!(mxcsr), + options(nostack, preserves_flags), + ); + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn read_x87_cw() -> u16 { + let mut cw: u16 = 0; + unsafe { + core::arch::asm!( + "fnstcw [{addr}]", + addr = in(reg) core::ptr::addr_of_mut!(cw), + options(nostack, preserves_flags), + ); + } + cw +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn write_x87_cw(cw: u16) { + unsafe { + core::arch::asm!( + "fldcw [{addr}]", + addr = in(reg) core::ptr::addr_of!(cw), + options(nostack, preserves_flags), + ); + } } /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn feclearexcept(excepts: c_int) -> c_int { - unimplemented!(); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + let masked = excepts & FE_ALL_EXCEPT; + if masked != 0 { + let mxcsr = read_mxcsr(); + write_mxcsr(mxcsr & !masked as u32); + } + } + let _ = excepts; + 0 } /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn fegetenv(envp: *mut fenv_t) -> c_int { - unimplemented!(); + if envp.is_null() { + return -1; + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + unsafe { + (*envp).x87_cw = read_x87_cw(); + (*envp)._reserved0 = 0; + (*envp).mxcsr = read_mxcsr(); + } + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + unsafe { + *envp = core::mem::zeroed(); + } + 0 } /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn fegetexceptflag(flagp: *mut fexcept_t, excepts: c_int) -> c_int { - unimplemented!(); + if flagp.is_null() { + return -1; + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + unsafe { + let mxcsr = read_mxcsr(); + *flagp = (mxcsr as c_int & excepts & FE_ALL_EXCEPT) as c_uint; + } + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + unsafe { + *flagp = 0; + } + let _ = excepts; + 0 } /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn fegetround() -> c_int { - FE_TONEAREST + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + let mxcsr = read_mxcsr(); + ((mxcsr >> 13) & 0x3) as c_int + } + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + { + FE_TONEAREST + } } /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn feholdexcept(envp: *mut fenv_t) -> c_int { - unimplemented!(); + if envp.is_null() { + return -1; + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + unsafe { + // Save current environment. + (*envp).x87_cw = read_x87_cw(); + (*envp)._reserved0 = 0; + (*envp).mxcsr = read_mxcsr(); + // Clear all exception flags and mask all exceptions (non-stop mode). + let mxcsr = read_mxcsr(); + write_mxcsr((mxcsr | FE_ALL_EXCEPT as u32 | (FE_ALL_EXCEPT as u32) << 7) & !(FE_ALL_EXCEPT as u32)); + } + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + unsafe { + *envp = core::mem::zeroed(); + } + 0 } /// See . // #[unsafe(no_mangle)] -pub unsafe extern "C" fn feraiseexcept(except: c_int) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn feraiseexcept(excepts: c_int) -> c_int { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + let masked = excepts & FE_ALL_EXCEPT; + if masked != 0 { + let mxcsr = read_mxcsr(); + // Set the exception flag bits. Unmask them first so the + // exception is actually raised (not just flagged). + write_mxcsr((mxcsr & !((masked as u32) << 7)) | masked as u32); + } + } + let _ = excepts; + 0 } /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn fesetenv(envp: *const fenv_t) -> c_int { - unimplemented!(); + if envp.is_null() { + return -1; + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + unsafe { + write_x87_cw((*envp).x87_cw); + write_mxcsr((*envp).mxcsr); + } + let _ = envp; + 0 } /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn fesetexceptflag(flagp: *const fexcept_t, excepts: c_int) -> c_int { - unimplemented!(); + if flagp.is_null() { + return -1; + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + unsafe { + let masked = excepts & FE_ALL_EXCEPT; + if masked != 0 { + let mxcsr = read_mxcsr(); + let new_flags = (*flagp as c_int) & masked; + write_mxcsr((mxcsr & !(masked as u32)) | new_flags as u32); + } + } + let _ = (flagp, excepts); + 0 } /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn fesetround(round: c_int) -> c_int { - unimplemented!(); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + if !(0..=3).contains(&round) { + return -1; + } + let mxcsr = read_mxcsr(); + write_mxcsr((mxcsr & !(0x3 << 13)) | ((round as u32) << 13)); + } + let _ = round; + 0 } /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn fetestexcept(excepts: c_int) -> c_int { - unimplemented!(); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + let mxcsr = read_mxcsr(); + (mxcsr as c_int) & excepts & FE_ALL_EXCEPT + } + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + { + let _ = excepts; + 0 + } } /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn feupdateenv(envp: *const fenv_t) -> c_int { - unimplemented!(); + if envp.is_null() { + return -1; + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + unsafe { + // POSIX: save current exceptions, restore the environment, then + // re-raise the saved exceptions. + let current = read_mxcsr() as c_int & FE_ALL_EXCEPT; + write_x87_cw((*envp).x87_cw); + write_mxcsr((*envp).mxcsr); + if current != 0 { + let mxcsr = read_mxcsr(); + write_mxcsr(mxcsr | current as u32); + } + } + let _ = envp; + 0 }