Skip to main content

c/
fenv.rs

1#[cfg(target_arch = "x86_64")]
2pub type fexcept_t = u16;
3
4#[cfg(target_arch = "aarch64")]
5pub type fexcept_t = u32;
6
7// x87 fnstenv/fldenv (28 bytes) + stmxcsr/ldmxcsr (4 bytes) = 32 bytes
8#[cfg(target_arch = "x86_64")]
9#[repr(C)]
10pub struct fenv_t {
11    __x87_env: [u8; 28],
12    __mxcsr: u32,
13}
14
15#[cfg(target_arch = "aarch64")]
16#[repr(C)]
17pub struct fenv_t {
18    __fpcr: u32,
19    __fpsr: u32,
20}
21
22#[cfg(target_arch = "x86_64")]
23const FE_DFL_ENV: *const fenv_t = -1isize as *const fenv_t;
24#[cfg(target_arch = "aarch64")]
25const FE_DFL_ENV: *const fenv_t = -1isize as *const fenv_t;
26
27#[cfg(target_arch = "x86_64")]
28mod feconst {
29    use super::*;
30    pub const FE_INVALID: c_int = 1;
31    pub const FE_DIVBYZERO: c_int = 4;
32    pub const FE_OVERFLOW: c_int = 8;
33    pub const FE_UNDERFLOW: c_int = 16;
34    pub const FE_INEXACT: c_int = 32;
35    pub const FE_ALL_EXCEPT: c_int = 63;
36
37    pub const FE_TONEAREST: c_int = 0;
38    pub const FE_DOWNWARD: c_int = 0x400;
39    pub const FE_UPWARD: c_int = 0x800;
40    pub const FE_TOWARDZERO: c_int = 0xc00;
41}
42
43#[cfg(target_arch = "aarch64")]
44mod feconst {
45    use super::*;
46    pub const FE_INVALID: c_int = 1;
47    pub const FE_DIVBYZERO: c_int = 2;
48    pub const FE_OVERFLOW: c_int = 4;
49    pub const FE_UNDERFLOW: c_int = 8;
50    pub const FE_INEXACT: c_int = 16;
51    pub const FE_ALL_EXCEPT: c_int = 31;
52
53    pub const FE_TONEAREST: c_int = 0;
54    pub const FE_DOWNWARD: c_int = 0x800000;
55    pub const FE_UPWARD: c_int = 0x400000;
56    pub const FE_TOWARDZERO: c_int = 0xc00000;
57}
58
59use feconst::*;
60
61// x86_64 implementation using x87/MXCSR
62#[cfg(target_arch = "x86_64")]
63mod x86_64_imp {
64    use super::*;
65
66    // CW=0x037f (all masked, nearest, extended), tags=0xffff (all empty)
67    const DEFAULT_X87_ENV: [u8; 28] = {
68        let mut env = [0u8; 28];
69        env[0] = 0x7f;
70        env[1] = 0x03;
71        env[8] = 0xff;
72        env[9] = 0xff;
73        env
74    };
75    const DEFAULT_MXCSR: u32 = 0x1f80;
76
77    // musl semantics: clears x87, merges old x87 into MXCSR then clears MXCSR
78    #[no_mangle]
79    pub unsafe extern "C" fn feclearexcept(excepts: c_int) -> c_int {
80        let mask = excepts & 0x3f;
81        let sw: u16;
82        core::arch::asm!("fnstsw ax", out("ax") sw);
83        if (sw as c_int) & mask != 0 {
84            core::arch::asm!("fnclex");
85        }
86        let mut mxcsr: u32 = 0;
87        let p = &mut mxcsr as *mut u32;
88        core::arch::asm!("stmxcsr [{p}]", p = in(reg) p);
89        let mxcsr_val = core::ptr::read(p) | ((sw as u32) & 0x3f);
90        if mxcsr_val & (mask as u32) != 0 {
91            core::ptr::write(p, mxcsr_val & !(mask as u32));
92            core::arch::asm!("ldmxcsr [{p}]", p = in(reg) p);
93        }
94        0
95    }
96
97    #[no_mangle]
98    pub unsafe extern "C" fn feraiseexcept(excepts: c_int) -> c_int {
99        let mask = (excepts & 0x3f) as u32;
100        let mut mxcsr: u32 = 0;
101        let p = &mut mxcsr as *mut u32;
102        core::arch::asm!("stmxcsr [{p}]", p = in(reg) p);
103        core::ptr::write(p, core::ptr::read(p) | mask);
104        core::arch::asm!("ldmxcsr [{p}]", p = in(reg) p);
105        0
106    }
107
108    #[no_mangle]
109    pub unsafe extern "C" fn fetestexcept(excepts: c_int) -> c_int {
110        let mask = excepts & 0x3f;
111        let mut mxcsr: u32 = 0;
112        let p = &mut mxcsr as *mut u32;
113        core::arch::asm!("stmxcsr [{p}]", p = in(reg) p);
114        let sw: u16;
115        core::arch::asm!("fnstsw ax", out("ax") sw);
116        ((sw as c_int) | (core::ptr::read(p) as c_int)) & mask
117    }
118
119    #[no_mangle]
120    pub unsafe extern "C" fn fegetround() -> c_int {
121        let mut mxcsr: u32 = 0;
122        let p = &mut mxcsr as *mut u32;
123        core::arch::asm!("stmxcsr [{p}]", p = in(reg) p);
124        // MXCSR bits 13-14 map to x87 CW bits 10-11
125        ((core::ptr::read(p) >> 3) & 0xc00) as c_int
126    }
127
128    #[no_mangle]
129    pub unsafe extern "C" fn fesetround(r: c_int) -> c_int {
130        if r != FE_TONEAREST && r != FE_DOWNWARD && r != FE_UPWARD && r != FE_TOWARDZERO {
131            return -1;
132        }
133        let mut cw: u16 = 0;
134        let p_cw = &mut cw as *mut u16;
135        core::arch::asm!("fnstcw [{p}]", p = in(reg) p_cw);
136        core::ptr::write(p_cw, (core::ptr::read(p_cw) & 0xf3ff) | (r as u16 & 0x0c00));
137        core::arch::asm!("fldcw [{p}]", p = in(reg) p_cw);
138
139        let mut mxcsr: u32 = 0;
140        let p_mx = &mut mxcsr as *mut u32;
141        core::arch::asm!("stmxcsr [{p}]", p = in(reg) p_mx);
142        core::ptr::write(p_mx, (core::ptr::read(p_mx) & 0xffff9fff) | ((r as u32 & 0xc00) << 3));
143        core::arch::asm!("ldmxcsr [{p}]", p = in(reg) p_mx);
144        0
145    }
146
147    #[no_mangle]
148    pub unsafe extern "C" fn fegetenv(envp: *mut fenv_t) -> c_int {
149        core::arch::asm!("fnstenv [{p}]", p = in(reg) envp);
150        let mxcsr_p = (envp as *mut u8).add(28) as *mut u32;
151        core::arch::asm!("stmxcsr [{p}]", p = in(reg) mxcsr_p);
152        0
153    }
154
155    #[no_mangle]
156    pub unsafe extern "C" fn fesetenv(envp: *const fenv_t) -> c_int {
157        if envp == FE_DFL_ENV {
158            core::arch::asm!("fldenv [{p}]", p = in(reg) DEFAULT_X87_ENV.as_ptr());
159            let p = &DEFAULT_MXCSR as *const u32;
160            core::arch::asm!("ldmxcsr [{p}]", p = in(reg) p);
161        } else {
162            core::arch::asm!("fldenv [{p}]", p = in(reg) envp);
163            let mxcsr_p = (envp as *const u8).add(28) as *const u32;
164            core::arch::asm!("ldmxcsr [{p}]", p = in(reg) mxcsr_p);
165        }
166        0
167    }
168}
169
170#[cfg(target_arch = "x86_64")]
171use x86_64_imp::*;
172
173// aarch64 implementation using FPCR/FPSR
174#[cfg(target_arch = "aarch64")]
175mod aarch64_imp {
176    use super::*;
177
178    #[no_mangle]
179    pub unsafe extern "C" fn feclearexcept(excepts: c_int) -> c_int {
180        let mask = (excepts & 0x1f) as u64;
181        let mut fpsr: u64;
182        core::arch::asm!("mrs {fpsr}, fpsr", fpsr = out(reg) fpsr);
183        let new_fpsr = fpsr & !mask;
184        core::arch::asm!("msr fpsr, {fpsr}", fpsr = in(reg) new_fpsr);
185        0
186    }
187
188    #[no_mangle]
189    pub unsafe extern "C" fn feraiseexcept(excepts: c_int) -> c_int {
190        let mask = (excepts & 0x1f) as u64;
191        let mut fpsr: u64;
192        core::arch::asm!("mrs {fpsr}, fpsr", fpsr = out(reg) fpsr);
193        let new_fpsr = fpsr | mask;
194        core::arch::asm!("msr fpsr, {fpsr}", fpsr = in(reg) new_fpsr);
195        0
196    }
197
198    #[no_mangle]
199    pub unsafe extern "C" fn fetestexcept(excepts: c_int) -> c_int {
200        let mask = (excepts & 0x1f) as u64;
201        let mut fpsr: u64;
202        core::arch::asm!("mrs {fpsr}, fpsr", fpsr = out(reg) fpsr);
203        (fpsr & mask) as c_int
204    }
205
206    #[no_mangle]
207    pub unsafe extern "C" fn fegetround() -> c_int {
208        let mut fpcr: u64;
209        core::arch::asm!("mrs {fpcr}, fpcr", fpcr = out(reg) fpcr);
210        (fpcr as c_int) & 0xc00000
211    }
212
213    #[no_mangle]
214    pub unsafe extern "C" fn fesetround(r: c_int) -> c_int {
215        if r != FE_TONEAREST && r != FE_DOWNWARD && r != FE_UPWARD && r != FE_TOWARDZERO {
216            return -1;
217        }
218        let mut fpcr: u64;
219        core::arch::asm!("mrs {fpcr}, fpcr", fpcr = out(reg) fpcr);
220        let new_fpcr = (fpcr & !0xc00000u64) | ((r as u64) & 0xc00000);
221        core::arch::asm!("msr fpcr, {fpcr}", fpcr = in(reg) new_fpcr);
222        0
223    }
224
225    #[no_mangle]
226    pub unsafe extern "C" fn fegetenv(envp: *mut fenv_t) -> c_int {
227        let mut fpcr: u64;
228        let mut fpsr: u64;
229        core::arch::asm!("mrs {fpcr}, fpcr", fpcr = out(reg) fpcr);
230        core::arch::asm!("mrs {fpsr}, fpsr", fpsr = out(reg) fpsr);
231        (*envp).__fpcr = fpcr as u32;
232        (*envp).__fpsr = fpsr as u32;
233        0
234    }
235
236    #[no_mangle]
237    pub unsafe extern "C" fn fesetenv(envp: *const fenv_t) -> c_int {
238        let (fpcr, fpsr) = if envp == FE_DFL_ENV {
239            (0u64, 0u64)
240        } else {
241            ((*envp).__fpcr as u64, (*envp).__fpsr as u64)
242        };
243        core::arch::asm!("msr fpcr, {fpcr}", fpcr = in(reg) fpcr);
244        core::arch::asm!("msr fpsr, {fpsr}", fpsr = in(reg) fpsr);
245        0
246    }
247}
248
249#[cfg(target_arch = "aarch64")]
250use aarch64_imp::*;
251
252#[no_mangle]
253pub unsafe extern "C" fn fegetexceptflag(fp: *mut fexcept_t, mask: c_int) -> c_int {
254    *fp = fetestexcept(mask) as fexcept_t;
255    0
256}
257
258#[no_mangle]
259pub unsafe extern "C" fn fesetexceptflag(fp: *const fexcept_t, mask: c_int) -> c_int {
260    let flags = *fp as c_int;
261    feclearexcept((!flags) & mask);
262    feraiseexcept(flags & mask);
263    0
264}
265
266#[no_mangle]
267pub unsafe extern "C" fn feholdexcept(envp: *mut fenv_t) -> c_int {
268    fegetenv(envp);
269    feclearexcept(FE_ALL_EXCEPT);
270    0
271}
272
273#[no_mangle]
274pub unsafe extern "C" fn feupdateenv(envp: *const fenv_t) -> c_int {
275    let ex = fetestexcept(FE_ALL_EXCEPT);
276    fesetenv(envp);
277    feraiseexcept(ex);
278    0
279}
280
281// C99 FLT_ROUNDS: 0=towardzero, 1=nearest, 2=upward, 3=downward
282#[no_mangle]
283pub extern "C" fn __flt_rounds() -> c_int {
284    match unsafe { fegetround() } {
285        FE_TOWARDZERO => 0,
286        FE_TONEAREST => 1,
287        FE_UPWARD => 2,
288        FE_DOWNWARD => 3,
289        _ => -1,
290    }
291}