relibc: implement _fenv POSIX functions (fenv.h)
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.
This commit is contained in:
+223
-20
@@ -1,85 +1,288 @@
|
|||||||
//! `fenv.h` implementation.
|
//! `fenv.h` implementation.
|
||||||
//!
|
//!
|
||||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
||||||
|
//!
|
||||||
|
//! 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 <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
|
||||||
pub const FE_ALL_EXCEPT: c_int = 0;
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
|
||||||
pub const FE_TONEAREST: c_int = 0;
|
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 <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
/// Stored FP exception flags. On x86 this is a subset of the MXCSR
|
||||||
pub type fexcept_t = c_ulonglong;
|
/// register containing the exception flag bits (bits 0-5).
|
||||||
|
pub type fexcept_t = c_uint;
|
||||||
|
|
||||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
|
/// 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)]
|
#[repr(C)]
|
||||||
pub struct fenv_t {
|
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feclearexcept.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feclearexcept.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn feclearexcept(excepts: c_int) -> c_int {
|
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetenv.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetenv.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn fegetenv(envp: *mut fenv_t) -> c_int {
|
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetexceptflag.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetexceptflag.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn fegetexceptflag(flagp: *mut fexcept_t, excepts: c_int) -> c_int {
|
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetround.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetround.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn fegetround() -> c_int {
|
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feholdexcept.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feholdexcept.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn feholdexcept(envp: *mut fenv_t) -> c_int {
|
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feraiseexcept.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feraiseexcept.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn feraiseexcept(except: c_int) -> c_int {
|
pub unsafe extern "C" fn feraiseexcept(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();
|
||||||
|
// 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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fesetenv.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fesetenv.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn fesetenv(envp: *const fenv_t) -> c_int {
|
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fesetexceptflag.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fesetexceptflag.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn fesetexceptflag(flagp: *const fexcept_t, excepts: c_int) -> c_int {
|
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetround.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetround.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn fesetround(round: c_int) -> c_int {
|
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fetestexcept.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fetestexcept.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn fetestexcept(excepts: c_int) -> c_int {
|
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feupdateenv.html>.
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feupdateenv.html>.
|
||||||
// #[unsafe(no_mangle)]
|
// #[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn feupdateenv(envp: *const fenv_t) -> c_int {
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user