git: d8b6461f8c56 - main - libm: Implement femode_t, fegetmode(), and fesetmode() as per C23

From: Faraz Vahedi <kfv_at_FreeBSD.org>
Date: Wed, 02 Sep 2026 23:39:28 UTC
The branch main has been updated by kfv:

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

commit d8b6461f8c56237a00ddc32678b3928bf76f5440
Author:     Faraz Vahedi <kfv@FreeBSD.org>
AuthorDate: 2026-09-02 23:35:22 +0000
Commit:     Faraz Vahedi <kfv@FreeBSD.org>
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 <kfv@FreeBSD.org>
+.\"
+.\" 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 ***