From nobody Wed Sep 02 23:39:28 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4hZzhx0Vmfz6qtDg for ; Wed, 02 Sep 2026 23:39:29 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hZzhw5gGhz4Fk3 for ; Wed, 02 Sep 2026 23:39:28 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1788392368; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=VjcVLnA8DMcGw3OqJDkMQ+TtxNQ4sX/TKZo8aceLAoo=; b=GnMq1Wm3+UH6vN4/HXlLHI60IH5ANE7xTMpBk1DeyX1Vlx8sEdn1MZ40JgLu/jeOdPKJQA POTjUKockogWZE0V/GjdLFtTVgVMFXnRzf+hIXtX0R6lIsAPrmIfVgP9BNOVXGLkx0+ecH pTEVi4oLqRcysZlTTc73O2fa+Yhvpy/nwvnjXFQEuRYIlgEye9b1VLF3y+GoxNmUPQ3b4V KNhKTP8US4GMChv1CtQK/FRA2FlG6ebxMfbCtHtzbDY4b+2WyBL4kd6G+ozb09P9jZWtPI VV4QI222e125TGqbnA+JEIOyZhHyF4jDdzGWW3gHkpP1b6VTMVx0h3c33ep+BA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1788392368; a=rsa-sha256; cv=none; b=s1Jis4MU8UYIhJdVU984VwbA7ihmZyoxzZaLzS+/TWl4IfGhxLE+Z2UML8rTK0q/c3caA9 uTjFz/ebyKhRjHM3Y32irfgCNHkteIaZLZQahZ6sP3BJVIHRB/vYaqQcsKy998NE1IyAR3 PDZ6XI6wHtteYpM+ai1eNaeXeIX2D77lHlqMLhYvLVbRo4zMgXmJxis+Os/jEBrCG8jS1M BKccBUx/mRJjAIRFNuNp2XTW8f/qQ0KJwmolMxmApAvWhmDA+mnCFIQ1hFhR3DQaKFbUi6 eMPBJAF6PTysJCsNU8TOwVZrEjgAwmLX1n4Q10EivTpy6jG5mJD31l/RPZQztg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1788392368; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=VjcVLnA8DMcGw3OqJDkMQ+TtxNQ4sX/TKZo8aceLAoo=; b=cItN/vp60V39cpm8gwiqapmDELXT4oyjZbmjzAyYHkcXM/2Prags8RiWAHhSSpjDUIhpaf UYmNiPWz/S5Npyu+z5+3rhimz29I1J4tVoeDEVOl4rr9nqQYjhq71S049ihn/v+2h/BWEc Uu5mrxvF4opd1H1zDXma4JJU7AwY92tujScLlm6uj3r5mUwFmhyKmwBziAK0R70COX7Hed AFyyQcO0/58tRsPcHzKTrZmbicH1TiA0NCBL3NPHyyAi81575B9XnufdDJAC7ucpCPpIbv iCd+nClrOyyOV6/fw4unnqo/LL6HYpi4Ev8X2ZEgiiDqsI53L5YXgYLH23uyxw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hZzhw45mnz14Tc for ; Wed, 02 Sep 2026 23:39:28 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 43140 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Wed, 02 Sep 2026 23:39:28 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Faraz Vahedi Subject: git: d8b6461f8c56 - main - libm: Implement femode_t, fegetmode(), and fesetmode() as per C23 List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kfv X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: d8b6461f8c56237a00ddc32678b3928bf76f5440 Auto-Submitted: auto-generated Date: Wed, 02 Sep 2026 23:39:28 +0000 Message-Id: <6a98b3b0.43140.6f98c870@gitrepo.freebsd.org> The branch main has been updated by kfv: URL: https://cgit.FreeBSD.org/src/commit/?id=d8b6461f8c56237a00ddc32678b3928bf76f5440 commit d8b6461f8c56237a00ddc32678b3928bf76f5440 Author: Faraz Vahedi AuthorDate: 2026-09-02 23:35:22 +0000 Commit: Faraz Vahedi 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 +.\" +.\" 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 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 ***