The branch main has been updated by kfv:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=d8b6461f8c56237a00ddc32678b3928bf76f5440

commit d8b6461f8c56237a00ddc32678b3928bf76f5440
Author:     Faraz Vahedi <[email protected]>
AuthorDate: 2026-09-02 23:35:22 +0000
Commit:     Faraz Vahedi <[email protected]>
CommitDate: 2026-09-02 23:35:22 +0000

    libm: Implement femode_t, fegetmode(), and fesetmode() as per C23
    
    Reviewed by:    kargl, kib
    Approved by:    fuz (mentor)
    MFC after:      1 month
    Differential Revision:  https://reviews.freebsd.org/D59288
---
 lib/msun/Makefile             |  3 +-
 lib/msun/Symbol.map           |  3 ++
 lib/msun/aarch64/fenv.c       | 13 ++++++++
 lib/msun/aarch64/fenv.h       | 29 ++++++++++++++++
 lib/msun/amd64/fenv.c         | 18 ++++++++++
 lib/msun/arm/fenv-mangle.h    |  2 ++
 lib/msun/arm/fenv.c           | 42 +++++++++++++++++++++++
 lib/msun/arm/fenv.h           | 33 ++++++++++++++++++
 lib/msun/i387/fenv.c          | 18 ++++++++++
 lib/msun/man/fegetmode.3      | 70 ++++++++++++++++++++++++++++++++++++++
 lib/msun/man/fegetround.3     |  4 ++-
 lib/msun/man/fenv.3           | 49 ++++++++++++++++++++++++++-
 lib/msun/powerpc/fenv.c       | 13 ++++++++
 lib/msun/powerpc/fenv.h       | 31 +++++++++++++++++
 lib/msun/riscv/fenv.c         | 13 ++++++++
 lib/msun/riscv/fenv.h         | 33 ++++++++++++++++++
 lib/msun/src/fenv-softfloat.h | 20 +++++++++++
 lib/msun/tests/fenv_test.c    | 63 ++++++++++++++++++++++++++++++++++
 lib/msun/x86/fenv.h           | 78 +++++++++++++++++++++++++++++++++++++++++++
 19 files changed, 532 insertions(+), 3 deletions(-)

diff --git a/lib/msun/Makefile b/lib/msun/Makefile
index 745819f5268f..6505d09cdaa5 100644
--- a/lib/msun/Makefile
+++ b/lib/msun/Makefile
@@ -187,7 +187,7 @@ MAN=        acos.3 acosh.3 acospi.3 asin.3 asinh.3 asinpi.3 
\
        cpow.3 csqrt.3 erf.3 \
        exp.3 fabs.3 fdim.3 \
        feclearexcept.3 feenableexcept.3 fegetenv.3 \
-       fegetround.3 fenv.3 floor.3 fma.3 \
+       fegetmode.3 fegetround.3 fenv.3 floor.3 fma.3 \
        fmax.3 fmaximum.3 fmaximum_mag.3 \
        fmaximum_mag_num.3 fmaximum_num.3 fmod.3 \
        hypot.3 ieee.3 ieee_test.3 ilogb.3 j0.3 \
@@ -241,6 +241,7 @@ MLINKS+=feclearexcept.3 fegetexceptflag.3 feclearexcept.3 
feraiseexcept.3 \
 MLINKS+=feenableexcept.3 fedisableexcept.3 feenableexcept.3 fegetexcept.3
 MLINKS+=fegetenv.3 feholdexcept.3 fegetenv.3 fesetenv.3 \
        fegetenv.3 feupdateenv.3
+MLINKS+=fegetmode.3 fesetmode.3
 MLINKS+=fegetround.3 fesetround.3
 MLINKS+=floor.3 floorf.3 floor.3 floorl.3
 MLINKS+=fma.3 fmaf.3 fma.3 fmal.3
diff --git a/lib/msun/Symbol.map b/lib/msun/Symbol.map
index 6f96c0d4f206..74d9b9a0f73f 100644
--- a/lib/msun/Symbol.map
+++ b/lib/msun/Symbol.map
@@ -329,6 +329,9 @@ FBSD_1.9 {
        atanpi;
        atanpif;
        atanpil;
+       __fe_dfl_mode;
+       fegetmode;
+       fesetmode;
        fmaximum;
        fmaximumf;
        fmaximuml;
diff --git a/lib/msun/aarch64/fenv.c b/lib/msun/aarch64/fenv.c
index 3d7e467880c7..5a6fa5933b4b 100644
--- a/lib/msun/aarch64/fenv.c
+++ b/lib/msun/aarch64/fenv.c
@@ -32,6 +32,7 @@
  * this as a default environment.
  */
 const fenv_t __fe_dfl_env = 0;
+const femode_t __fe_dfl_mode = 0;
 
 int
 (feclearexcept)(int excepts)
@@ -75,6 +76,18 @@ int
        return (__fesetround_int(round));
 }
 
+int
+(fegetmode)(femode_t *modep)
+{
+       return (__fegetmode_int(modep));
+}
+
+int
+(fesetmode)(const femode_t *modep)
+{
+       return (__fesetmode_int(modep));
+}
+
 int
 (fegetenv)(fenv_t *envp)
 {
diff --git a/lib/msun/aarch64/fenv.h b/lib/msun/aarch64/fenv.h
index aebcd99909ce..67d968bc849b 100644
--- a/lib/msun/aarch64/fenv.h
+++ b/lib/msun/aarch64/fenv.h
@@ -40,6 +40,7 @@
 
 /* The high 32 bits contain fpcr, low 32 contain fpsr. */
 typedef        __uint64_t      fenv_t;
+typedef        __uint32_t      femode_t;
 typedef        __uint64_t      fexcept_t;
 
 /* Exception flags */
@@ -71,6 +72,10 @@ __BEGIN_DECLS
 extern const fenv_t    __fe_dfl_env;
 #define        FE_DFL_ENV      (&__fe_dfl_env)
 
+/* Default floating-point control modes */
+extern const femode_t  __fe_dfl_mode;
+#define        FE_DFL_MODE     (&__fe_dfl_mode)
+
 /* We need to be able to map status flag positions to mask flag positions */
 #define _FPUSW_SHIFT   8
 #define        _ENABLE_MASK    (FE_ALL_EXCEPT << _FPUSW_SHIFT)
@@ -88,6 +93,8 @@ int feraiseexcept(int);
 int fetestexcept(int);
 int fegetround(void);
 int fesetround(int);
+int fegetmode(femode_t *);
+int fesetmode(const femode_t *);
 int fegetenv(fenv_t *);
 int feholdexcept(fenv_t *);
 int fesetenv(const fenv_t *);
@@ -112,6 +119,8 @@ int fegetexcept(void);
 #define        fetestexcept(a)         __fetestexcept_int(a)
 #define        fegetround()            __fegetround_int()
 #define        fesetround(a)           __fesetround_int(a)
+#define        fegetmode(m)            __fegetmode_int(m)
+#define        fesetmode(m)            __fesetmode_int(m)
 #define        fegetenv(e)             __fegetenv_int(e)
 #define        feholdexcept(e)         __feholdexcept_int(e)
 #define        fesetenv(e)             __fesetenv_int(e)
@@ -197,6 +206,26 @@ __fesetround_int(int __round)
        return (0);
 }
 
+__fenv_static inline int
+__fegetmode_int(femode_t *__modep)
+{
+       __uint64_t __fpcr;
+
+       __mrs_fpcr(__fpcr);
+       *__modep = (__uint32_t)__fpcr;
+       return (0);
+}
+
+__fenv_static inline int
+__fesetmode_int(const femode_t *__modep)
+{
+       __uint64_t __fpcr;
+
+       __fpcr = *__modep;
+       __msr_fpcr(__fpcr);
+       return (0);
+}
+
 __fenv_static inline int
 __fegetenv_int(fenv_t *__envp)
 {
diff --git a/lib/msun/amd64/fenv.c b/lib/msun/amd64/fenv.c
index fe9ecfff3b83..cba16af35340 100644
--- a/lib/msun/amd64/fenv.c
+++ b/lib/msun/amd64/fenv.c
@@ -41,6 +41,12 @@ const fenv_t __fe_dfl_env = {
        __INITIAL_MXCSR__
 };
 
+const femode_t __fe_dfl_mode = {
+       .__control = __INITIAL_FPUCW__,
+       .__reserved = 0,
+       .__mxcsr = __INITIAL_MXCSR__
+};
+
 int
 (feclearexcept)(int excepts)
 {
@@ -99,6 +105,18 @@ int
        return (__fesetround_int(round));
 }
 
+int
+(fegetmode)(femode_t *modep)
+{
+       return (__fegetmode_int(modep));
+}
+
+int
+(fesetmode)(const femode_t *modep)
+{
+       return (__fesetmode_int(modep));
+}
+
 int
 fegetenv(fenv_t *envp)
 {
diff --git a/lib/msun/arm/fenv-mangle.h b/lib/msun/arm/fenv-mangle.h
index ad8c022356ab..2e40c0d80c6d 100644
--- a/lib/msun/arm/fenv-mangle.h
+++ b/lib/msun/arm/fenv-mangle.h
@@ -41,6 +41,8 @@
 #define        fetestexcept    FENV_MANGLE(fetestexcept)
 #define        fegetround      FENV_MANGLE(fegetround)
 #define        fesetround      FENV_MANGLE(fesetround)
+#define        fegetmode       FENV_MANGLE(fegetmode)
+#define        fesetmode       FENV_MANGLE(fesetmode)
 #define        fegetenv        FENV_MANGLE(fegetenv)
 #define        feholdexcept    FENV_MANGLE(feholdexcept)
 #define        fesetenv        FENV_MANGLE(fesetenv)
diff --git a/lib/msun/arm/fenv.c b/lib/msun/arm/fenv.c
index 2cf5bbdea28f..28823244cbae 100644
--- a/lib/msun/arm/fenv.c
+++ b/lib/msun/arm/fenv.c
@@ -43,6 +43,7 @@
  * this as a default environment.
  */
 const fenv_t __fe_dfl_env = 0;
+const femode_t __fe_dfl_mode = 0;
 #endif
 
 
@@ -107,6 +108,18 @@ int
        return (__fesetround_int(round));
 }
 
+int
+(fegetmode)(femode_t *modep)
+{
+       return (__fegetmode_int(modep));
+}
+
+int
+(fesetmode)(const femode_t *modep)
+{
+       return (__fesetmode_int(modep));
+}
+
 int
 (fegetenv)(fenv_t *envp)
 {
@@ -160,6 +173,8 @@ int __softfp_feraiseexcept(int __excepts);
 int __softfp_fetestexcept(int __excepts);
 int __softfp_fegetround(void);
 int __softfp_fesetround(int __round);
+int __softfp_fegetmode(femode_t *__modep);
+int __softfp_fesetmode(const femode_t *__modep);
 int __softfp_fegetenv(fenv_t *__envp);
 int __softfp_feholdexcept(fenv_t *__envp);
 int __softfp_fesetenv(const fenv_t *__envp);
@@ -175,6 +190,8 @@ int __vfp_feraiseexcept(int __excepts);
 int __vfp_fetestexcept(int __excepts);
 int __vfp_fegetround(void);
 int __vfp_fesetround(int __round);
+int __vfp_fegetmode(femode_t *__modep);
+int __vfp_fesetmode(const femode_t *__modep);
 int __vfp_fegetenv(fenv_t *__envp);
 int __vfp_feholdexcept(fenv_t *__envp);
 int __vfp_fesetenv(const fenv_t *__envp);
@@ -298,6 +315,31 @@ int
        return (0);
 }
 
+int
+(fegetmode)(femode_t *__modep)
+{
+       femode_t __vfp_modep;
+
+       __vfp_modep = 0;
+       if (_libc_arm_fpu_present)
+               __vfp_fegetmode(&__vfp_modep);
+       __softfp_fegetmode(__modep);
+       *__modep |= __vfp_modep;
+
+       return (0);
+}
+
+int
+(fesetmode)(const femode_t *__modep)
+{
+
+       if (_libc_arm_fpu_present)
+               __vfp_fesetmode(__modep);
+       __softfp_fesetmode(__modep);
+
+       return (0);
+}
+
 int
 (fegetenv)(fenv_t *__envp)
 {
diff --git a/lib/msun/arm/fenv.h b/lib/msun/arm/fenv.h
index 6febbf95008d..8d1c9716b165 100644
--- a/lib/msun/arm/fenv.h
+++ b/lib/msun/arm/fenv.h
@@ -37,6 +37,7 @@
 #endif
 
 typedef        __uint32_t      fenv_t;
+typedef        __uint32_t      femode_t;
 typedef        __uint32_t      fexcept_t;
 
 /* Exception flags */
@@ -79,6 +80,10 @@ __BEGIN_DECLS
 extern const fenv_t    __fe_dfl_env;
 #define        FE_DFL_ENV      (&__fe_dfl_env)
 
+/* Default floating-point control modes */
+extern const femode_t  __fe_dfl_mode;
+#define        FE_DFL_MODE     (&__fe_dfl_mode)
+
 /* We need to be able to map status flag positions to mask flag positions */
 #ifndef __ARM_PCS_VFP
 #define        _FPUSW_SHIFT    16
@@ -94,6 +99,8 @@ int feraiseexcept(int __excepts);
 int fetestexcept(int __excepts);
 int fegetround(void);
 int fesetround(int __round);
+int fegetmode(femode_t *__modep);
+int fesetmode(const femode_t *__modep);
 int fegetenv(fenv_t *__envp);
 int feholdexcept(fenv_t *__envp);
 int fesetenv(const fenv_t *__envp);
@@ -118,6 +125,8 @@ int feraiseexcept(int);
 int fetestexcept(int);
 int fegetround(void);
 int fesetround(int);
+int fegetmode(femode_t *);
+int fesetmode(const femode_t *);
 int fegetenv(fenv_t *);
 int feholdexcept(fenv_t *);
 int fesetenv(const fenv_t *);
@@ -145,6 +154,8 @@ int fegetexcept(void);
 #define        fetestexcept(a)         __fetestexcept_int(a)
 #define        fegetround()            __fegetround_int()
 #define        fesetround(a)           __fesetround_int(a)
+#define        fegetmode(m)            __fegetmode_int(m)
+#define        fesetmode(m)            __fesetmode_int(m)
 #define        fegetenv(e)             __fegetenv_int(e)
 #define        feholdexcept(e)         __feholdexcept_int(e)
 #define        fesetenv(e)             __fesetenv_int(e)
@@ -228,6 +239,28 @@ __fesetround_int(int __round)
        return (0);
 }
 
+__fenv_static inline int
+__fegetmode_int(femode_t *__modep)
+{
+       fenv_t __fpsr;
+
+       vmrs_fpscr(__fpsr);
+       *__modep = __fpsr & ~FE_ALL_EXCEPT;
+       return (0);
+}
+
+__fenv_static inline int
+__fesetmode_int(const femode_t *__modep)
+{
+       fenv_t __fpsr;
+
+       vmrs_fpscr(__fpsr);
+       __fpsr &= FE_ALL_EXCEPT;
+       __fpsr |= *__modep & ~FE_ALL_EXCEPT;
+       vmsr_fpscr(__fpsr);
+       return (0);
+}
+
 __fenv_static inline int
 __fegetenv_int(fenv_t *__envp)
 {
diff --git a/lib/msun/i387/fenv.c b/lib/msun/i387/fenv.c
index bae8de2fb969..d0b089085d72 100644
--- a/lib/msun/i387/fenv.c
+++ b/lib/msun/i387/fenv.c
@@ -41,6 +41,12 @@ const fenv_t __fe_dfl_env = {
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }
 };
 
+const femode_t __fe_dfl_mode = {
+       .__control = __INITIAL_NPXCW__,
+       .__reserved = 0,
+       .__mxcsr = __INITIAL_MXCSR__
+};
+
 enum __sse_support __has_sse =
 #ifdef __SSE__
        __SSE_YES;
@@ -144,6 +150,18 @@ int
        return (__fesetround_int(round));
 }
 
+int
+(fegetmode)(femode_t *modep)
+{
+       return (__fegetmode_int(modep));
+}
+
+int
+(fesetmode)(const femode_t *modep)
+{
+       return (__fesetmode_int(modep));
+}
+
 int
 fegetenv(fenv_t *envp)
 {
diff --git a/lib/msun/man/fegetmode.3 b/lib/msun/man/fegetmode.3
new file mode 100644
index 000000000000..465b9dc0d665
--- /dev/null
+++ b/lib/msun/man/fegetmode.3
@@ -0,0 +1,70 @@
+.\"
+.\" Copyright (c) 2026 Faraz Vahedi <[email protected]>
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd August 31, 2026
+.Dt FEGETMODE 3
+.Os
+.Sh NAME
+.Nm fegetmode ,
+.Nm fesetmode
+.Nd save and restore floating-point control modes
+.Sh LIBRARY
+.Lb libm
+.Sh SYNOPSIS
+.In fenv.h
+.Fd "#pragma STDC FENV_ACCESS ON"
+.Ft int
+.Fn fegetmode "femode_t *modep"
+.Ft int
+.Fn fesetmode "const femode_t *modep"
+.Sh DESCRIPTION
+The
+.Vt femode_t
+type represents the dynamic floating-point control modes,
+including the rounding direction and, where supported, exception
+masks and other implementation-defined modes.
+.Pp
+The
+.Fn fegetmode
+function stores the current control modes in the object
+pointed to by
+.Fa modep .
+.Pp
+The
+.Fn fesetmode
+function establishes the control modes represented by
+.Fa modep .
+The argument
+.Fa modep
+must point to an object set by a prior call to
+.Fn fegetmode ,
+or equal
+.Dv FE_DFL_MODE .
+.Dv FE_DFL_MODE
+expands to a pointer to the default control modes
+.Po
+installed at program startup
+.Pc .
+.Sh RETURN VALUES
+The
+.Fn fegetmode
+and
+.Fn fesetmode
+functions return 0 if they succeed, and non-zero otherwise;
+however, the present implementation always succeeds.
+.Sh SEE ALSO
+.Xr fenv 3 ,
+.Xr fegetround 3 ,
+.Xr fesetround 3
+.Sh STANDARDS
+The
+.Fn fegetmode
+and
+.Fn fesetmode
+functions conform to
+.St -isoC-2023 .
+.Sh HISTORY
+These routines first appeared in
+.Fx 16.0 .
diff --git a/lib/msun/man/fegetround.3 b/lib/msun/man/fegetround.3
index b41ef93322a1..435884a5788c 100644
--- a/lib/msun/man/fegetround.3
+++ b/lib/msun/man/fegetround.3
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 8, 2004
+.Dd August 31, 2026
 .Dt FEGETROUND 3
 .Os
 .Sh NAME
@@ -62,6 +62,8 @@ function returns 0 on success and non-zero otherwise;
 however, the present implementation always succeeds.
 .Sh SEE ALSO
 .Xr fenv 3 ,
+.Xr fegetmode 3 ,
+.Xr fesetmode 3 ,
 .Xr fpgetround 3 ,
 .Xr fpsetround 3
 .Sh STANDARDS
diff --git a/lib/msun/man/fenv.3 b/lib/msun/man/fenv.3
index ee67ef29bae1..bdf0f87d641c 100644
--- a/lib/msun/man/fenv.3
+++ b/lib/msun/man/fenv.3
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 16, 2005
+.Dd August 31, 2026
 .Dt FENV 3
 .Os
 .Sh NAME
@@ -31,6 +31,8 @@
 .Nm feraiseexcept ,
 .Nm fesetexceptflag ,
 .Nm fetestexcept ,
+.Nm fegetmode ,
+.Nm fesetmode ,
 .Nm fegetround ,
 .Nm fesetround ,
 .Nm fegetenv ,
@@ -57,6 +59,10 @@
 .Ft int
 .Fn fetestexcept "int excepts"
 .Ft int
+.Fn fegetmode "femode_t *modep"
+.Ft int
+.Fn fesetmode "const femode_t *modep"
+.Ft int
 .Fn fegetround void
 .Ft int
 .Fn fesetround "int round"
@@ -170,6 +176,29 @@ The
 and
 .Fn fesetround
 functions query and set the rounding mode.
+.Ss Control Modes
+The
+.Vt femode_t
+type represents the collection of dynamic floating-point control modes,
+including the rounding direction and, where supported, exception masks
+and other implementation-defined modes.
+The
+.Fn fegetmode
+and
+.Fn fesetmode
+functions save and restore all the implementation's dynamic
+floating-point control modes, respectively.
+Passing
+.Dv FE_DFL_MODE
+to
+.Fn fesetmode
+restores the default control modes as installed at program startup.
+.Pp
+The macro
+.Dv FE_DFL_MODE
+expands to a pointer to the constant variable of type
+.Vt femode_t
+that is initialized with the default control modes.
 .Ss Environment Control
 The
 .Fn fegetenv
@@ -241,11 +270,13 @@ double sqrt(double n) {
 .Xr fegetenv 3 ,
 .Xr fegetexcept 3 ,
 .Xr fegetexceptflag 3 ,
+.Xr fegetmode 3 ,
 .Xr fegetround 3 ,
 .Xr feholdexcept 3 ,
 .Xr feraiseexcept 3 ,
 .Xr fesetenv 3 ,
 .Xr fesetexceptflag 3 ,
+.Xr fesetmode 3 ,
 .Xr fesetround 3 ,
 .Xr fetestexcept 3 ,
 .Xr feupdateenv 3 ,
@@ -257,6 +288,16 @@ Except as noted below,
 conforms to
 .St -isoC-99 .
 The
+.Vt femode_t
+type, the
+.Dv FE_DFL_MODE
+macro, and the
+.Fn fegetmode
+and
+.Fn fesetmode
+functions conform to
+.St -isoC-2023 .
+The
 .Fn feenableexcept ,
 .Fn fedisableexcept ,
 and
@@ -271,6 +312,12 @@ It supersedes the non-standard routines defined in
 .In ieeefp.h
 and documented in
 .Xr fpgetround 3 .
+The
+.Fn fegetmode
+and
+.Fn fesetmode
+functions first appeared in
+.Fx 16.0 .
 .Sh CAVEATS
 The FENV_ACCESS pragma can be enabled with
 .Dl "#pragma STDC FENV_ACCESS ON"
diff --git a/lib/msun/powerpc/fenv.c b/lib/msun/powerpc/fenv.c
index 3104b59d4dfb..5f2d81ffd298 100644
--- a/lib/msun/powerpc/fenv.c
+++ b/lib/msun/powerpc/fenv.c
@@ -29,6 +29,7 @@
 #include "fenv.h"
 
 const fenv_t __fe_dfl_env = 0x00000000;
+const femode_t __fe_dfl_mode = 0x00000000;
 
 int
 (feclearexcept)(int excepts)
@@ -72,6 +73,18 @@ int
        return (__fesetround_int(round));
 }
 
+int
+(fegetmode)(femode_t *modep)
+{
+       return (__fegetmode_int(modep));
+}
+
+int
+(fesetmode)(const femode_t *modep)
+{
+       return (__fesetmode_int(modep));
+}
+
 int
 (fegetenv)(fenv_t *envp)
 {
diff --git a/lib/msun/powerpc/fenv.h b/lib/msun/powerpc/fenv.h
index 8752be09994c..de294fc3daad 100644
--- a/lib/msun/powerpc/fenv.h
+++ b/lib/msun/powerpc/fenv.h
@@ -37,6 +37,7 @@
 #endif
 
 typedef        __uint32_t      fenv_t;
+typedef        __uint32_t      femode_t;
 typedef        __uint32_t      fexcept_t;
 
 /* Exception flags */
@@ -84,6 +85,10 @@ __BEGIN_DECLS
 extern const fenv_t    __fe_dfl_env;
 #define        FE_DFL_ENV      (&__fe_dfl_env)
 
+/* Default floating-point control modes */
+extern const femode_t  __fe_dfl_mode;
+#define        FE_DFL_MODE     (&__fe_dfl_mode)
+
 /* We need to be able to map status flag positions to mask flag positions */
 #define        _ENABLE_MASK    ((FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \
                         FE_OVERFLOW | FE_UNDERFLOW) >> _FPUSW_SHIFT)
@@ -118,6 +123,8 @@ int feraiseexcept(int);
 int fetestexcept(int);
 int fegetround(void);
 int fesetround(int);
+int fegetmode(femode_t *);
+int fesetmode(const femode_t *);
 int fegetenv(fenv_t *);
 int feholdexcept(fenv_t *);
 int fesetenv(const fenv_t *);
@@ -139,6 +146,8 @@ int feupdateenv(const fenv_t *);
 #define        fetestexcept(a)         __fetestexcept_int(a)
 #define        fegetround()            __fegetround_int()
 #define        fesetround(a)           __fesetround_int(a)
+#define        fegetmode(m)            __fegetmode_int(m)
+#define        fesetmode(m)            __fesetmode_int(m)
 #define        fegetenv(e)             __fegetenv_int(e)
 #define        feholdexcept(e)         __feholdexcept_int(e)
 #define        fesetenv(e)             __fesetenv_int(e)
@@ -227,6 +236,28 @@ __fesetround_int(int __round)
        return (0);
 }
 
+__fenv_static inline int
+__fegetmode_int(femode_t *__modep)
+{
+       union __fpscr __r;
+
+       __mffs(&__r);
+       *__modep = __r.__bits.__reg & ~FE_ALL_EXCEPT;
+       return (0);
+}
+
+__fenv_static inline int
+__fesetmode_int(const femode_t *__modep)
+{
+       union __fpscr __r;
+
+       __mffs(&__r);
+       __r.__bits.__reg &= FE_ALL_EXCEPT;
+       __r.__bits.__reg |= *__modep & ~FE_ALL_EXCEPT;
+       __mtfsf(__r);
+       return (0);
+}
+
 __fenv_static inline int
 __fegetenv_int(fenv_t *__envp)
 {
diff --git a/lib/msun/riscv/fenv.c b/lib/msun/riscv/fenv.c
index 7fdedca1f09c..4a15e27ea8cb 100644
--- a/lib/msun/riscv/fenv.c
+++ b/lib/msun/riscv/fenv.c
@@ -31,6 +31,7 @@
  * this as a default environment.
  */
 const fenv_t __fe_dfl_env = 0;
+const femode_t __fe_dfl_mode = 0;
 
 int
 (feclearexcept)(int excepts)
@@ -74,6 +75,18 @@ int
        return (__fesetround_int(round));
 }
 
+int
+(fegetmode)(femode_t *modep)
+{
+       return (__fegetmode_int(modep));
+}
+
+int
+(fesetmode)(const femode_t *modep)
+{
+       return (__fesetmode_int(modep));
+}
+
 int
 (fegetenv)(fenv_t *envp)
 {
diff --git a/lib/msun/riscv/fenv.h b/lib/msun/riscv/fenv.h
index 199ca1806684..ea87e73fffb1 100644
--- a/lib/msun/riscv/fenv.h
+++ b/lib/msun/riscv/fenv.h
@@ -44,6 +44,7 @@
 #endif
 
 typedef        __uint64_t      fenv_t;
+typedef        __uint64_t      femode_t;
 typedef        __uint64_t      fexcept_t;
 
 /* Exception flags */
@@ -72,6 +73,10 @@ __BEGIN_DECLS
 extern const fenv_t    __fe_dfl_env;
 #define        FE_DFL_ENV      (&__fe_dfl_env)
 
+/* Default floating-point control modes */
+extern const femode_t  __fe_dfl_mode;
+#define        FE_DFL_MODE     (&__fe_dfl_mode)
+
 #ifndef __riscv_float_abi_double
 #error only double hard float ABI supported
 #endif
@@ -86,6 +91,8 @@ int feraiseexcept(int);
 int fetestexcept(int);
 int fegetround(void);
 int fesetround(int);
+int fegetmode(femode_t *);
+int fesetmode(const femode_t *);
 int fegetenv(fenv_t *);
 int feholdexcept(fenv_t *);
 int fesetenv(const fenv_t *);
@@ -107,6 +114,8 @@ int feupdateenv(const fenv_t *);
 #define        fetestexcept(a)         __fetestexcept_int(a)
 #define        fegetround()            __fegetround_int()
 #define        fesetround(a)           __fesetround_int(a)
+#define        fegetmode(m)            __fegetmode_int(m)
+#define        fesetmode(m)            __fesetmode_int(m)
 #define        fegetenv(e)             __fegetenv_int(e)
 #define        feholdexcept(e)         __feholdexcept_int(e)
 #define        fesetenv(e)             __fesetenv_int(e)
@@ -190,6 +199,30 @@ __fesetround_int(int __round)
        return (0);
 }
 
+__fenv_static inline int
+__fegetmode_int(femode_t *__modep)
+{
+       fexcept_t __fcsr;
+
+       __rfs(__fcsr);
+       *__modep = __fcsr & ~FE_ALL_EXCEPT;
+
+       return (0);
+}
+
+__fenv_static inline int
+__fesetmode_int(const femode_t *__modep)
+{
+       fexcept_t __fcsr;
+
+       __rfs(__fcsr);
+       __fcsr &= FE_ALL_EXCEPT;
+       __fcsr |= *__modep & ~FE_ALL_EXCEPT;
+       __wfs(__fcsr);
+
+       return (0);
+}
+
 __fenv_static inline int
 __fegetenv_int(fenv_t *__envp)
 {
diff --git a/lib/msun/src/fenv-softfloat.h b/lib/msun/src/fenv-softfloat.h
index b3974760a2eb..2807ccdcec1d 100644
--- a/lib/msun/src/fenv-softfloat.h
+++ b/lib/msun/src/fenv-softfloat.h
@@ -36,11 +36,13 @@
  * you must write an <fenv.h> that provides the following:
  *
  *   - a typedef for fenv_t, which may be an integer or struct type
+ *   - a typedef for femode_t, which may be an integer or struct type
  *   - a typedef for fexcept_t (XXX This file assumes fexcept_t is a
  *     simple integer type containing the exception mask.)
  *   - definitions of FE_* constants for the five exceptions and four
  *     rounding modes in IEEE 754, as described in fenv(3)
  *   - a definition, and the corresponding external symbol, for FE_DFL_ENV
+ *   - a definition, and the corresponding external symbol, for FE_DFL_MODE
  *   - a macro __set_env(env, flags, mask, rnd), which sets the given fenv_t
  *     from the exception flags, mask, and rounding mode
  *   - macros __env_flags(env), __env_mask(env), and __env_round(env), which
@@ -112,6 +114,24 @@ fesetround(int __round)
        return (0);
 }
 
+__fenv_static inline int
+__fegetmode_int(femode_t *__modep)
+{
+
+       __set_env(*__modep, 0, __softfloat_float_exception_mask,
+           __softfloat_float_rounding_mode);
+       return (0);
+}
+
+__fenv_static inline int
+__fesetmode_int(const femode_t *__modep)
+{
+
+       __softfloat_float_exception_mask = __env_mask(*__modep);
+       __softfloat_float_rounding_mode = __env_round(*__modep);
+       return (0);
+}
+
 __fenv_static inline int
 fegetenv(fenv_t *__envp)
 {
diff --git a/lib/msun/tests/fenv_test.c b/lib/msun/tests/fenv_test.c
index d85671827d37..f0dc5d6c2dd2 100644
--- a/lib/msun/tests/fenv_test.c
+++ b/lib/msun/tests/fenv_test.c
@@ -332,6 +332,68 @@ ATF_TC_BODY(fegsetround, tc)
        ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
 }
 
+/*
+ * Test fegetmode() and fesetmode().
+ *
+ * Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround()
+ */
+ATF_TC_WITHOUT_HEAD(fegsetmode);
+ATF_TC_BODY(fegsetmode, tc)
+{
+       femode_t mode_dfl, mode_down, mode_nomask, mode_mask;
+       int excepts;
+
+       ATF_CHECK(FE_DFL_MODE != NULL);
+       ATF_CHECK_EQ(0, fegetmode(&mode_dfl));
+       ATF_CHECK_EQ(FE_TONEAREST, fegetround());
+
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+       raiseexcept(FE_INVALID | FE_DIVBYZERO);
+       excepts = fetestexcept(ALL_STD_EXCEPT);
+       ATF_CHECK(excepts != 0);
+
+       ATF_CHECK_EQ(0, fesetround(FE_DOWNWARD));
+       ATF_CHECK_EQ(0, fegetmode(&mode_down));
+
+       ATF_CHECK_EQ(0, fesetmode(FE_DFL_MODE));
+       ATF_CHECK_EQ(FE_TONEAREST, fegetround());
+       ATF_CHECK_EQ(excepts, fetestexcept(ALL_STD_EXCEPT));
+
+       ATF_CHECK_EQ(0, fesetmode(&mode_down));
+       ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
+       ATF_CHECK_EQ(FE_DOWNWARD, getround());
+       ATF_CHECK_EQ(excepts, fetestexcept(ALL_STD_EXCEPT));
+
+       ATF_CHECK_EQ(0, fesetmode(&mode_dfl));
+       ATF_CHECK_EQ(FE_TONEAREST, fegetround());
+       ATF_CHECK_EQ(excepts, fetestexcept(ALL_STD_EXCEPT));
+
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+       ATF_CHECK_EQ(0, fesetround(FE_TONEAREST));
+
+       /*
+        * Exception masks are control modes.  Skip the restore check
+        * when the CPU ignores trap-enable bits, as on some AArch64
+        * implementations.
+        */
+       ATF_CHECK_EQ(0, fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT);
+       ATF_CHECK_EQ(0, fegetexcept() & ALL_STD_EXCEPT);
+       ATF_CHECK_EQ(0, fegetmode(&mode_nomask));
+       (void)feenableexcept(FE_INVALID);
+       if ((fegetexcept() & FE_INVALID) != 0) {
+               ATF_CHECK_EQ(0, fegetmode(&mode_mask));
+               ATF_CHECK_EQ(0, fesetmode(&mode_nomask));
+               ATF_CHECK_EQ(0, fegetexcept() & ALL_STD_EXCEPT);
+               ATF_CHECK_EQ(0, fesetmode(&mode_mask));
+               ATF_CHECK_EQ(FE_INVALID, fegetexcept() & ALL_STD_EXCEPT);
+               ATF_CHECK_EQ(0, fesetmode(FE_DFL_MODE));
+               ATF_CHECK_EQ(0, fegetexcept() & ALL_STD_EXCEPT);
+       }
+       (void)fedisableexcept(FE_ALL_EXCEPT);
+       ATF_CHECK_EQ(0, fesetround(FE_TONEAREST));
+       ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+}
+
 /*
  * Test fegetenv() and fesetenv().
  *
@@ -556,6 +618,7 @@ ATF_TP_ADD_TCS(tp)
        ATF_TP_ADD_TC(tp, fegsetexceptflag);
        ATF_TP_ADD_TC(tp, feraiseexcept);
        ATF_TP_ADD_TC(tp, fegsetround);
+       ATF_TP_ADD_TC(tp, fegsetmode);
        ATF_TP_ADD_TC(tp, fegsetenv);
        ATF_TP_ADD_TC(tp, masking);
        ATF_TP_ADD_TC(tp, feholdupdate);
diff --git a/lib/msun/x86/fenv.h b/lib/msun/x86/fenv.h
index cb44624a858a..137a635fd7a6 100644
--- a/lib/msun/x86/fenv.h
+++ b/lib/msun/x86/fenv.h
@@ -90,12 +90,27 @@ typedef struct {
 } fenv_t;
 #endif /* __i386__ */
 
+/*
+ * Control modes: x87 control word plus MXCSR bits other than the
+ * exception flags.  fesetmode() restores those bits without changing
+ * the raised exception flags.
+ */
+typedef struct {
+       __uint16_t      __control;
+       __uint16_t      __reserved;
+       __uint32_t      __mxcsr;
+} femode_t;
+
 __BEGIN_DECLS
 
 /* Default floating-point environment */
 extern const fenv_t    __fe_dfl_env;
 #define        FE_DFL_ENV      (&__fe_dfl_env)
 
+/* Default floating-point control modes */
+extern const femode_t  __fe_dfl_mode;
+#define        FE_DFL_MODE     (&__fe_dfl_mode)
+
 #define        __fldenvx(__env)        __asm __volatile("fldenv %0" : : "m" 
(__env)  \
                                : "st", "st(1)", "st(2)", "st(3)", "st(4)",   \
                                "st(5)", "st(6)", "st(7)")
@@ -148,6 +163,8 @@ int fegetexceptflag(fexcept_t *, int);
 int fetestexcept(int);
 int fesetround(int);
 int fegetround(void);
+int fegetmode(femode_t *);
*** 86 LINES SKIPPED ***

Reply via email to