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#[cfg(target_arch = "riscv64")]
8pub type fexcept_t = u32;
9
10// x87 fnstenv/fldenv (28 bytes) + stmxcsr/ldmxcsr (4 bytes) = 32 bytes
11#[cfg(target_arch = "x86_64")]
12#[repr(C)]
13pub struct fenv_t {
14    __x87_env: [u8; 28],
15    __mxcsr: u32,
16}
17
18#[cfg(target_arch = "aarch64")]
19#[repr(C)]
20pub struct fenv_t {
21    __fpcr: u32,
22    __fpsr: u32,
23}
24
25#[cfg(target_arch = "riscv64")]
26pub type fenv_t = u32;
27
28#[cfg(target_arch = "x86_64")]
29const FE_DFL_ENV: *const fenv_t = -1isize as *const fenv_t;
30#[cfg(target_arch = "aarch64")]
31const FE_DFL_ENV: *const fenv_t = -1isize as *const fenv_t;
32#[cfg(target_arch = "riscv64")]
33const FE_DFL_ENV: *const fenv_t = -1isize as *const fenv_t;
34
35#[cfg(target_arch = "x86_64")]
36mod feconst {
37    use super::*;
38    pub const FE_INVALID: c_int = 1;
39    pub const FE_DIVBYZERO: c_int = 4;
40    pub const FE_OVERFLOW: c_int = 8;
41    pub const FE_UNDERFLOW: c_int = 16;
42    pub const FE_INEXACT: c_int = 32;
43    pub const FE_ALL_EXCEPT: c_int = 63;
44
45    pub const FE_TONEAREST: c_int = 0;
46    pub const FE_DOWNWARD: c_int = 0x400;
47    pub const FE_UPWARD: c_int = 0x800;
48    pub const FE_TOWARDZERO: c_int = 0xc00;
49}
50
51#[cfg(target_arch = "aarch64")]
52mod feconst {
53    use super::*;
54    pub const FE_INVALID: c_int = 1;
55    pub const FE_DIVBYZERO: c_int = 2;
56    pub const FE_OVERFLOW: c_int = 4;
57    pub const FE_UNDERFLOW: c_int = 8;
58    pub const FE_INEXACT: c_int = 16;
59    pub const FE_ALL_EXCEPT: c_int = 31;
60
61    pub const FE_TONEAREST: c_int = 0;
62    pub const FE_DOWNWARD: c_int = 0x800000;
63    pub const FE_UPWARD: c_int = 0x400000;
64    pub const FE_TOWARDZERO: c_int = 0xc00000;
65}
66
67#[cfg(target_arch = "riscv64")]
68mod feconst {
69    use super::*;
70    pub const FE_INVALID: c_int = 16;
71    pub const FE_DIVBYZERO: c_int = 8;
72    pub const FE_OVERFLOW: c_int = 4;
73    pub const FE_UNDERFLOW: c_int = 2;
74    pub const FE_INEXACT: c_int = 1;
75    pub const FE_ALL_EXCEPT: c_int = 31;
76
77    pub const FE_TONEAREST: c_int = 0;
78    pub const FE_DOWNWARD: c_int = 2;
79    pub const FE_UPWARD: c_int = 3;
80    pub const FE_TOWARDZERO: c_int = 1;
81}
82
83use feconst::*;
84
85// x86_64 implementation using x87/MXCSR
86#[cfg(target_arch = "x86_64")]
87mod x86_64_imp {
88    use super::*;
89
90    // CW=0x037f (all masked, nearest, extended), tags=0xffff (all empty)
91    const DEFAULT_X87_ENV: [u8; 28] = {
92        let mut env = [0u8; 28];
93        env[0] = 0x7f;
94        env[1] = 0x03;
95        env[8] = 0xff;
96        env[9] = 0xff;
97        env
98    };
99    const DEFAULT_MXCSR: u32 = 0x1f80;
100
101    // musl semantics: clears x87, merges old x87 into MXCSR then clears MXCSR
102    #[no_mangle]
103    pub unsafe extern "C" fn feclearexcept(excepts: c_int) -> c_int {
104        let mask = excepts & 0x3f;
105        let sw: u16;
106        core::arch::asm!("fnstsw ax", out("ax") sw);
107        if (sw as c_int) & mask != 0 {
108            core::arch::asm!("fnclex");
109        }
110        let mut mxcsr: u32 = 0;
111        let p = &mut mxcsr as *mut u32;
112        core::arch::asm!("stmxcsr [{p}]", p = in(reg) p);
113        let mxcsr_val = core::ptr::read(p) | ((sw as u32) & 0x3f);
114        if mxcsr_val & (mask as u32) != 0 {
115            core::ptr::write(p, mxcsr_val & !(mask as u32));
116            core::arch::asm!("ldmxcsr [{p}]", p = in(reg) p);
117        }
118        0
119    }
120
121    #[no_mangle]
122    pub unsafe extern "C" fn feraiseexcept(excepts: c_int) -> c_int {
123        let mask = (excepts & 0x3f) as u32;
124        let mut mxcsr: u32 = 0;
125        let p = &mut mxcsr as *mut u32;
126        core::arch::asm!("stmxcsr [{p}]", p = in(reg) p);
127        core::ptr::write(p, core::ptr::read(p) | mask);
128        core::arch::asm!("ldmxcsr [{p}]", p = in(reg) p);
129        0
130    }
131
132    #[no_mangle]
133    pub unsafe extern "C" fn fetestexcept(excepts: c_int) -> c_int {
134        let mask = excepts & 0x3f;
135        let mut mxcsr: u32 = 0;
136        let p = &mut mxcsr as *mut u32;
137        core::arch::asm!("stmxcsr [{p}]", p = in(reg) p);
138        let sw: u16;
139        core::arch::asm!("fnstsw ax", out("ax") sw);
140        ((sw as c_int) | (core::ptr::read(p) as c_int)) & mask
141    }
142
143    #[no_mangle]
144    pub unsafe extern "C" fn fegetround() -> c_int {
145        let mut mxcsr: u32 = 0;
146        let p = &mut mxcsr as *mut u32;
147        core::arch::asm!("stmxcsr [{p}]", p = in(reg) p);
148        // MXCSR bits 13-14 map to x87 CW bits 10-11
149        ((core::ptr::read(p) >> 3) & 0xc00) as c_int
150    }
151
152    #[no_mangle]
153    pub unsafe extern "C" fn fesetround(r: c_int) -> c_int {
154        if r != FE_TONEAREST && r != FE_DOWNWARD && r != FE_UPWARD && r != FE_TOWARDZERO {
155            return -1;
156        }
157        let mut cw: u16 = 0;
158        let p_cw = &mut cw as *mut u16;
159        core::arch::asm!("fnstcw [{p}]", p = in(reg) p_cw);
160        core::ptr::write(p_cw, (core::ptr::read(p_cw) & 0xf3ff) | (r as u16 & 0x0c00));
161        core::arch::asm!("fldcw [{p}]", p = in(reg) p_cw);
162
163        let mut mxcsr: u32 = 0;
164        let p_mx = &mut mxcsr as *mut u32;
165        core::arch::asm!("stmxcsr [{p}]", p = in(reg) p_mx);
166        core::ptr::write(p_mx, (core::ptr::read(p_mx) & 0xffff9fff) | ((r as u32 & 0xc00) << 3));
167        core::arch::asm!("ldmxcsr [{p}]", p = in(reg) p_mx);
168        0
169    }
170
171    #[no_mangle]
172    pub unsafe extern "C" fn fegetenv(envp: *mut fenv_t) -> c_int {
173        core::arch::asm!("fnstenv [{p}]", p = in(reg) envp);
174        let mxcsr_p = (envp as *mut u8).add(28) as *mut u32;
175        core::arch::asm!("stmxcsr [{p}]", p = in(reg) mxcsr_p);
176        0
177    }
178
179    #[no_mangle]
180    pub unsafe extern "C" fn fesetenv(envp: *const fenv_t) -> c_int {
181        if envp == FE_DFL_ENV {
182            core::arch::asm!("fldenv [{p}]", p = in(reg) DEFAULT_X87_ENV.as_ptr());
183            let p = &DEFAULT_MXCSR as *const u32;
184            core::arch::asm!("ldmxcsr [{p}]", p = in(reg) p);
185        } else {
186            core::arch::asm!("fldenv [{p}]", p = in(reg) envp);
187            let mxcsr_p = (envp as *const u8).add(28) as *const u32;
188            core::arch::asm!("ldmxcsr [{p}]", p = in(reg) mxcsr_p);
189        }
190        0
191    }
192}
193
194#[cfg(target_arch = "x86_64")]
195use x86_64_imp::*;
196
197// aarch64 implementation using FPCR/FPSR
198#[cfg(target_arch = "aarch64")]
199mod aarch64_imp {
200    use super::*;
201
202    #[no_mangle]
203    pub unsafe extern "C" fn feclearexcept(excepts: c_int) -> c_int {
204        let mask = (excepts & 0x1f) as u64;
205        let mut fpsr: u64;
206        core::arch::asm!("mrs {fpsr}, fpsr", fpsr = out(reg) fpsr);
207        let new_fpsr = fpsr & !mask;
208        core::arch::asm!("msr fpsr, {fpsr}", fpsr = in(reg) new_fpsr);
209        0
210    }
211
212    #[no_mangle]
213    pub unsafe extern "C" fn feraiseexcept(excepts: c_int) -> c_int {
214        let mask = (excepts & 0x1f) as u64;
215        let mut fpsr: u64;
216        core::arch::asm!("mrs {fpsr}, fpsr", fpsr = out(reg) fpsr);
217        let new_fpsr = fpsr | mask;
218        core::arch::asm!("msr fpsr, {fpsr}", fpsr = in(reg) new_fpsr);
219        0
220    }
221
222    #[no_mangle]
223    pub unsafe extern "C" fn fetestexcept(excepts: c_int) -> c_int {
224        let mask = (excepts & 0x1f) as u64;
225        let mut fpsr: u64;
226        core::arch::asm!("mrs {fpsr}, fpsr", fpsr = out(reg) fpsr);
227        (fpsr & mask) as c_int
228    }
229
230    #[no_mangle]
231    pub unsafe extern "C" fn fegetround() -> c_int {
232        let mut fpcr: u64;
233        core::arch::asm!("mrs {fpcr}, fpcr", fpcr = out(reg) fpcr);
234        (fpcr as c_int) & 0xc00000
235    }
236
237    #[no_mangle]
238    pub unsafe extern "C" fn fesetround(r: c_int) -> c_int {
239        if r != FE_TONEAREST && r != FE_DOWNWARD && r != FE_UPWARD && r != FE_TOWARDZERO {
240            return -1;
241        }
242        let mut fpcr: u64;
243        core::arch::asm!("mrs {fpcr}, fpcr", fpcr = out(reg) fpcr);
244        let new_fpcr = (fpcr & !0xc00000u64) | ((r as u64) & 0xc00000);
245        core::arch::asm!("msr fpcr, {fpcr}", fpcr = in(reg) new_fpcr);
246        0
247    }
248
249    #[no_mangle]
250    pub unsafe extern "C" fn fegetenv(envp: *mut fenv_t) -> c_int {
251        let mut fpcr: u64;
252        let mut fpsr: u64;
253        core::arch::asm!("mrs {fpcr}, fpcr", fpcr = out(reg) fpcr);
254        core::arch::asm!("mrs {fpsr}, fpsr", fpsr = out(reg) fpsr);
255        (*envp).__fpcr = fpcr as u32;
256        (*envp).__fpsr = fpsr as u32;
257        0
258    }
259
260    #[no_mangle]
261    pub unsafe extern "C" fn fesetenv(envp: *const fenv_t) -> c_int {
262        let (fpcr, fpsr) = if envp == FE_DFL_ENV {
263            (0u64, 0u64)
264        } else {
265            ((*envp).__fpcr as u64, (*envp).__fpsr as u64)
266        };
267        core::arch::asm!("msr fpcr, {fpcr}", fpcr = in(reg) fpcr);
268        core::arch::asm!("msr fpsr, {fpsr}", fpsr = in(reg) fpsr);
269        0
270    }
271}
272
273#[cfg(target_arch = "aarch64")]
274use aarch64_imp::*;
275
276// riscv64 implementation using fflags/frm/fcsr CSRs
277#[cfg(target_arch = "riscv64")]
278mod riscv64_imp {
279    use super::*;
280
281    #[no_mangle]
282    pub unsafe extern "C" fn feclearexcept(excepts: c_int) -> c_int {
283        let mask = (excepts & 0x1f) as u64;
284        // csrc fflags, a0 — clear bits in fflags
285        core::arch::asm!(
286            "csrc fflags, {mask}",
287            mask = in(reg) mask,
288            options(nostack),
289        );
290        0
291    }
292
293    #[no_mangle]
294    pub unsafe extern "C" fn feraiseexcept(excepts: c_int) -> c_int {
295        let mask = (excepts & 0x1f) as u64;
296        // csrs fflags, a0 — set bits in fflags
297        core::arch::asm!(
298            "csrs fflags, {mask}",
299            mask = in(reg) mask,
300            options(nostack),
301        );
302        0
303    }
304
305    #[no_mangle]
306    pub unsafe extern "C" fn fetestexcept(excepts: c_int) -> c_int {
307        let fflags: u64;
308        // frflags t0 — read fflags
309        core::arch::asm!(
310            "frflags {fflags}",
311            fflags = out(reg) fflags,
312            options(nostack),
313        );
314        (fflags as c_int) & (excepts & 0x1f)
315    }
316
317    #[no_mangle]
318    pub unsafe extern "C" fn fegetround() -> c_int {
319        let frm: u64;
320        // frrm a0 — read rounding mode
321        core::arch::asm!(
322            "frrm {frm}",
323            frm = out(reg) frm,
324            options(nostack),
325        );
326        frm as c_int
327    }
328
329    #[no_mangle]
330    pub unsafe extern "C" fn fesetround(r: c_int) -> c_int {
331        if r != FE_TONEAREST && r != FE_DOWNWARD && r != FE_UPWARD && r != FE_TOWARDZERO {
332            return -1;
333        }
334        // fsrm t0, a0 — set rounding mode
335        core::arch::asm!(
336            "fsrm zero, {r}",
337            r = in(reg) r as u64,
338            options(nostack),
339        );
340        0
341    }
342
343    #[no_mangle]
344    pub unsafe extern "C" fn fegetenv(envp: *mut fenv_t) -> c_int {
345        let fcsr: u64;
346        // frcsr t0 — read full CSR
347        core::arch::asm!(
348            "frcsr {fcsr}",
349            fcsr = out(reg) fcsr,
350            options(nostack),
351        );
352        *envp = fcsr as u32;
353        0
354    }
355
356    #[no_mangle]
357    pub unsafe extern "C" fn fesetenv(envp: *const fenv_t) -> c_int {
358        let val = if envp == FE_DFL_ENV {
359            0u64
360        } else {
361            *envp as u64
362        };
363        // fscsr t1 — set full CSR
364        core::arch::asm!(
365            "fscsr zero, {val}",
366            val = in(reg) val,
367            options(nostack),
368        );
369        0
370    }
371}
372
373#[cfg(target_arch = "riscv64")]
374use riscv64_imp::*;
375
376#[no_mangle]
377pub unsafe extern "C" fn fegetexceptflag(fp: *mut fexcept_t, mask: c_int) -> c_int {
378    *fp = fetestexcept(mask) as fexcept_t;
379    0
380}
381
382#[no_mangle]
383pub unsafe extern "C" fn fesetexceptflag(fp: *const fexcept_t, mask: c_int) -> c_int {
384    let flags = *fp as c_int;
385    feclearexcept((!flags) & mask);
386    feraiseexcept(flags & mask);
387    0
388}
389
390#[no_mangle]
391pub unsafe extern "C" fn feholdexcept(envp: *mut fenv_t) -> c_int {
392    fegetenv(envp);
393    feclearexcept(FE_ALL_EXCEPT);
394    0
395}
396
397#[no_mangle]
398pub unsafe extern "C" fn feupdateenv(envp: *const fenv_t) -> c_int {
399    let ex = fetestexcept(FE_ALL_EXCEPT);
400    fesetenv(envp);
401    feraiseexcept(ex);
402    0
403}
404
405// C99 FLT_ROUNDS: 0=towardzero, 1=nearest, 2=upward, 3=downward
406#[no_mangle]
407pub extern "C" fn __flt_rounds() -> c_int {
408    match unsafe { fegetround() } {
409        FE_TOWARDZERO => 0,
410        FE_TONEAREST => 1,
411        FE_UPWARD => 2,
412        FE_DOWNWARD => 3,
413        _ => -1,
414    }
415}