diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -22,10 +22,16 @@ # fenv.h entrypoints libc.src.fenv.feclearexcept + libc.src.fenv.fegetenv + libc.src.fenv.fegetexceptflag libc.src.fenv.fegetround + libc.src.fenv.feholdexcept + libc.src.fenv.fesetenv + libc.src.fenv.fesetexceptflag libc.src.fenv.fesetround libc.src.fenv.feraiseexcept libc.src.fenv.fetestexcept + libc.src.fenv.feupdateenv # stdlib.h entrypoints libc.src.stdlib.abs diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -179,6 +179,29 @@ ]; } +def FEnvT : TypeDecl<"fenv_t"> { + let Decl = [{ + #ifdef __aarch64__ + typedef struct { + unsigned char __control_word[4]; + unsigned char __status_word[4]; + } fenv_t; + #endif + #ifdef __x86_64__ + typedef struct { + unsigned char __x86_status[28]; + unsigned char __mxcsr[4]; + } fenv_t; + #endif + }]; +} + +def FExceptT : TypeDecl<"fexcept_t"> { + let Decl = [{ + typedef int fexcept_t; + }]; +} + def FenvAPI: PublicAPI<"fenv.h"> { let Macros = [ SimpleMacroDef<"FE_DIVBYZERO", "1">, @@ -193,6 +216,10 @@ SimpleMacroDef<"FE_TOWARDZERO", "4">, SimpleMacroDef<"FE_UPWARD", "8">, ]; + let TypeDeclarations = [ + FEnvT, + FExceptT, + ]; } def StringAPI : PublicAPI<"string.h"> { diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -25,10 +25,16 @@ # fenv.h entrypoints libc.src.fenv.feclearexcept + libc.src.fenv.fegetenv + libc.src.fenv.fegetexceptflag libc.src.fenv.fegetround + libc.src.fenv.feholdexcept + libc.src.fenv.fesetenv + libc.src.fenv.fesetexceptflag libc.src.fenv.fesetround libc.src.fenv.feraiseexcept libc.src.fenv.fetestexcept + libc.src.fenv.feupdateenv # signal.h entrypoints libc.src.signal.raise diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -96,6 +96,12 @@ ] >; + NamedType FEnvT = NamedType<"fenv_t">; + PtrType FEnvTPtr = PtrType; + ConstType ConstFEnvTPtr = ConstType; + NamedType FExceptT = NamedType<"fexcept_t">; + PtrType FExceptTPtr = PtrType; + ConstType ConstFExceptTPtr = ConstType; HeaderSpec Fenv = HeaderSpec< "fenv.h", [ @@ -111,7 +117,10 @@ Macro<"FE_TOWARDZERO">, Macro<"FE_UPWARD"> ], - [], // Types + [ + NamedType<"fenv_t">, + NamedType<"fexcept_t">, + ], // Types [], // Enumerations [ FunctionSpec< @@ -139,6 +148,36 @@ RetValSpec, [] >, + FunctionSpec< + "fegetenv", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "fesetenv", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "fegetexceptflag", + RetValSpec, + [ArgSpec, ArgSpec] + >, + FunctionSpec< + "fesetexceptflag", + RetValSpec, + [ArgSpec, ArgSpec] + >, + FunctionSpec< + "feholdexcept", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "feupdateenv", + RetValSpec, + [ArgSpec] + >, ] >; diff --git a/libc/src/fenv/CMakeLists.txt b/libc/src/fenv/CMakeLists.txt --- a/libc/src/fenv/CMakeLists.txt +++ b/libc/src/fenv/CMakeLists.txt @@ -62,3 +62,81 @@ COMPILE_OPTIONS -O2 ) + +add_entrypoint_object( + fegetenv + SRCS + fegetenv.cpp + HDRS + fegetenv.h + DEPENDS + libc.include.fenv + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + fesetenv + SRCS + fesetenv.cpp + HDRS + fesetenv.h + DEPENDS + libc.include.fenv + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + fegetexceptflag + SRCS + fegetexceptflag.cpp + HDRS + fegetexceptflag.h + DEPENDS + libc.include.fenv + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + fesetexceptflag + SRCS + fesetexceptflag.cpp + HDRS + fesetexceptflag.h + DEPENDS + libc.include.fenv + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + feholdexcept + SRCS + feholdexcept.cpp + HDRS + feholdexcept.h + DEPENDS + libc.include.fenv + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + feupdateenv + SRCS + feupdateenv.cpp + HDRS + feupdateenv.h + DEPENDS + libc.include.fenv + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) diff --git a/libc/src/fenv/fegetenv.h b/libc/src/fenv/fegetenv.h new file mode 100644 --- /dev/null +++ b/libc/src/fenv/fegetenv.h @@ -0,0 +1,20 @@ +//===-- Implementation header for fegetenv ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_FENV_FEGETENV_H +#define LLVM_LIBC_SRC_FENV_FEGETENV_H + +#include + +namespace __llvm_libc { + +int fegetenv(fenv_t *); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_FENV_FEGETENV_H diff --git a/libc/src/fenv/fegetenv.cpp b/libc/src/fenv/fegetenv.cpp new file mode 100644 --- /dev/null +++ b/libc/src/fenv/fegetenv.cpp @@ -0,0 +1,19 @@ +//===-- Implementation of fegetenv function -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/fenv/fegetenv.h" +#include "src/__support/common.h" +#include "utils/FPUtil/FEnv.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, fegetenv, (fenv_t * envp)) { + return fputil::getEnv(envp); +} + +} // namespace __llvm_libc diff --git a/libc/src/fenv/fegetexceptflag.h b/libc/src/fenv/fegetexceptflag.h new file mode 100644 --- /dev/null +++ b/libc/src/fenv/fegetexceptflag.h @@ -0,0 +1,20 @@ +//===-- Implementation header for fegetexceptflag ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_FENV_FEGETEXCEPTFLAG_H +#define LLVM_LIBC_SRC_FENV_FEGETEXCEPTFLAG_H + +#include + +namespace __llvm_libc { + +int fegetexceptflag(fexcept_t *, int excepts); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_FENV_FEGETEXCEPTFLAG_H diff --git a/libc/src/fenv/fegetexceptflag.cpp b/libc/src/fenv/fegetexceptflag.cpp new file mode 100644 --- /dev/null +++ b/libc/src/fenv/fegetexceptflag.cpp @@ -0,0 +1,26 @@ +//===-- Implementation of fegetexceptflag function ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/fenv/fegetexceptflag.h" +#include "src/__support/common.h" +#include "utils/FPUtil/FEnv.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, fegetexceptflag, (fexcept_t * flagp, int excepts)) { + // Since the return type of fetestexcept is int, we ensure that fexcept_t + // matches in size. + static_assert(sizeof(int) == sizeof(fexcept_t), + "sizeof(fexcept_t) != sizeof(int)"); + *reinterpret_cast(flagp) = fputil::testExcept(FE_ALL_EXCEPT) & excepts; + return 0; +} + +} // namespace __llvm_libc diff --git a/libc/src/fenv/feholdexcept.h b/libc/src/fenv/feholdexcept.h new file mode 100644 --- /dev/null +++ b/libc/src/fenv/feholdexcept.h @@ -0,0 +1,20 @@ +//===-- Implementation header for feholdexcept ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_FENV_FEHOLDEXCEPT_H +#define LLVM_LIBC_SRC_FENV_FEHOLDEXCEPT_H + +#include + +namespace __llvm_libc { + +int feholdexcept(fenv_t *); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_FENV_FEHOLDEXCEPT_H diff --git a/libc/src/fenv/feholdexcept.cpp b/libc/src/fenv/feholdexcept.cpp new file mode 100644 --- /dev/null +++ b/libc/src/fenv/feholdexcept.cpp @@ -0,0 +1,25 @@ +//===-- Implementation of feholdexcept function ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/fenv/feholdexcept.h" +#include "src/__support/common.h" +#include "utils/FPUtil/FEnv.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, feholdexcept, (fenv_t * envp)) { + if (fputil::getEnv(envp) != 0) + return -1; + fputil::clearExcept(FE_ALL_EXCEPT); + fputil::disableExcept(FE_ALL_EXCEPT); + return 0; +} + +} // namespace __llvm_libc diff --git a/libc/src/fenv/fesetenv.h b/libc/src/fenv/fesetenv.h new file mode 100644 --- /dev/null +++ b/libc/src/fenv/fesetenv.h @@ -0,0 +1,20 @@ +//===-- Implementation header for fesetenv ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_FENV_FESETENV_H +#define LLVM_LIBC_SRC_FENV_FESETENV_H + +#include + +namespace __llvm_libc { + +int fesetenv(const fenv_t *); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_FENV_FESETENV_H diff --git a/libc/src/fenv/fesetenv.cpp b/libc/src/fenv/fesetenv.cpp new file mode 100644 --- /dev/null +++ b/libc/src/fenv/fesetenv.cpp @@ -0,0 +1,19 @@ +//===-- Implementation of fesetenv function -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/fenv/fesetenv.h" +#include "src/__support/common.h" +#include "utils/FPUtil/FEnv.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, fesetenv, (const fenv_t *envp)) { + return fputil::setEnv(envp); +} + +} // namespace __llvm_libc diff --git a/libc/src/fenv/fesetexceptflag.h b/libc/src/fenv/fesetexceptflag.h new file mode 100644 --- /dev/null +++ b/libc/src/fenv/fesetexceptflag.h @@ -0,0 +1,20 @@ +//===-- Implementation header for fesetexceptflag ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_FENV_FESETEXCEPTFLAG_H +#define LLVM_LIBC_SRC_FENV_FESETEXCEPTFLAG_H + +#include + +namespace __llvm_libc { + +int fesetexceptflag(const fexcept_t *, int excepts); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_FENV_FESETEXCEPTFLAG_H diff --git a/libc/src/fenv/fesetexceptflag.cpp b/libc/src/fenv/fesetexceptflag.cpp new file mode 100644 --- /dev/null +++ b/libc/src/fenv/fesetexceptflag.cpp @@ -0,0 +1,27 @@ +//===-- Implementation of fesetexceptflag function ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/fenv/fesetexceptflag.h" +#include "src/__support/common.h" +#include "utils/FPUtil/FEnv.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, fesetexceptflag, + (const fexcept_t *flagp, int excepts)) { + // Since the return type of fetestexcept is int, we ensure that fexcept_t + // matches in size. + static_assert(sizeof(int) == sizeof(fexcept_t), + "sizeof(fexcept_t) != sizeof(int)"); + int excepts_to_set = *reinterpret_cast(flagp) & excepts; + return fputil::setExcept(excepts_to_set); +} + +} // namespace __llvm_libc diff --git a/libc/src/fenv/feupdateenv.h b/libc/src/fenv/feupdateenv.h new file mode 100644 --- /dev/null +++ b/libc/src/fenv/feupdateenv.h @@ -0,0 +1,20 @@ +//===-- Implementation header for feupdateenv -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_FENV_FEUPDATEENV_H +#define LLVM_LIBC_SRC_FENV_FEUPDATEENV_H + +#include + +namespace __llvm_libc { + +int feupdateenv(const fenv_t *); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_FENV_FEUPDATEENV_H diff --git a/libc/src/fenv/feupdateenv.cpp b/libc/src/fenv/feupdateenv.cpp new file mode 100644 --- /dev/null +++ b/libc/src/fenv/feupdateenv.cpp @@ -0,0 +1,24 @@ +//===-- Implementation of feupdateenv function ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/fenv/feupdateenv.h" +#include "src/__support/common.h" +#include "utils/FPUtil/FEnv.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, feupdateenv, (const fenv_t *envp)) { + int current_excepts = fputil::testExcept(FE_ALL_EXCEPT); + if (fputil::setEnv(envp) != 0) + return -1; + return fputil::raiseExcept(current_excepts); +} + +} // namespace __llvm_libc diff --git a/libc/test/src/fenv/CMakeLists.txt b/libc/test/src/fenv/CMakeLists.txt --- a/libc/test/src/fenv/CMakeLists.txt +++ b/libc/test/src/fenv/CMakeLists.txt @@ -24,6 +24,42 @@ libc.utils.FPUtil.fputil ) +add_libc_unittest( + getenv_and_setenv_test + SUITE + libc_fenv_unittests + SRCS + getenv_and_setenv_test.cpp + DEPENDS + libc.src.fenv.fegetenv + libc.src.fenv.fesetenv + libc.utils.FPUtil.fputil +) + +add_libc_unittest( + exception_flags_test + SUITE + libc_fenv_unittests + SRCS + exception_flags_test.cpp + DEPENDS + libc.src.fenv.fegetexceptflag + libc.src.fenv.fesetexceptflag + libc.utils.FPUtil.fputil +) + +add_libc_unittest( + feupdateenv_test + SUITE + libc_fenv_unittests + SRCS + feupdateenv_test.cpp + DEPENDS + libc.include.signal + libc.src.fenv.feupdateenv + libc.utils.FPUtil.fputil +) + if (NOT LLVM_USE_SANITIZER) # Sanitizers don't like SIGFPE. So, we will run the # tests which raise SIGFPE only in non-sanitizer builds. @@ -40,4 +76,16 @@ libc.src.fenv.fetestexcept libc.utils.FPUtil.fputil ) + + add_libc_unittest( + feholdexcept_test + SUITE + libc_fenv_unittests + SRCS + feholdexcept_test.cpp + DEPENDS + libc.include.signal + libc.src.fenv.feholdexcept + libc.utils.FPUtil.fputil + ) endif() diff --git a/libc/test/src/fenv/exception_flags_test.cpp b/libc/test/src/fenv/exception_flags_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/fenv/exception_flags_test.cpp @@ -0,0 +1,45 @@ +//===-- Unittests for fegetexceptflag and fesetexceptflag -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/fenv/fegetexceptflag.h" +#include "src/fenv/fesetexceptflag.h" + +#include "utils/FPUtil/FEnv.h" +#include "utils/UnitTest/Test.h" + +#include + +TEST(LlvmLibcFenvTest, GetExceptFlagAndSetExceptFlag) { + // We will disable all exceptions to prevent invocation of the exception + // handler. + __llvm_libc::fputil::disableExcept(FE_ALL_EXCEPT); + + int excepts[] = {FE_DIVBYZERO, FE_INVALID, FE_INEXACT, FE_OVERFLOW, + FE_UNDERFLOW}; + + for (int e : excepts) { + // The overall idea is to raise an except and save the exception flags. + // Next, clear the flags and then set the saved exception flags. This + // should set the flag corresponding to the previously raised exception. + __llvm_libc::fputil::raiseExcept(e); + // Make sure that the exception flag is set. + ASSERT_NE(__llvm_libc::fputil::testExcept(FE_ALL_EXCEPT) & e, 0); + + fexcept_t eflags; + ASSERT_EQ(__llvm_libc::fegetexceptflag(&eflags, FE_ALL_EXCEPT), 0); + + __llvm_libc::fputil::clearExcept(e); + ASSERT_EQ(__llvm_libc::fputil::testExcept(FE_ALL_EXCEPT) & e, 0); + + ASSERT_EQ(__llvm_libc::fesetexceptflag(&eflags, FE_ALL_EXCEPT), 0); + ASSERT_NE(__llvm_libc::fputil::testExcept(FE_ALL_EXCEPT) & e, 0); + + // Cleanup + __llvm_libc::fputil::clearExcept(e); + } +} diff --git a/libc/test/src/fenv/feholdexcept_test.cpp b/libc/test/src/fenv/feholdexcept_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/fenv/feholdexcept_test.cpp @@ -0,0 +1,37 @@ +//===-- Unittests for feholdexcept with exceptions enabled ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/fenv/feholdexcept.h" + +#include "utils/FPUtil/FEnv.h" +#include "utils/UnitTest/Test.h" + +#include +#include + +TEST(LlvmLibcFEnvTest, RaiseAndCrash) { + int excepts[] = {FE_DIVBYZERO, FE_INVALID, FE_INEXACT, FE_OVERFLOW, + FE_UNDERFLOW}; + + for (int e : excepts) { + fenv_t env; + __llvm_libc::fputil::disableExcept(FE_ALL_EXCEPT); + __llvm_libc::fputil::enableExcept(e); + ASSERT_EQ(__llvm_libc::fputil::clearExcept(FE_ALL_EXCEPT), 0); + ASSERT_EQ(__llvm_libc::feholdexcept(&env), 0); + // feholdexcept should disable all excepts so raising an exception + // should not crash/invoke the exception handler. + ASSERT_EQ(__llvm_libc::fputil::raiseExcept(e), 0); + + // When we put back the saved env which has the exception enabled, it + // should crash with SIGFPE. + __llvm_libc::fputil::setEnv(&env); + ASSERT_DEATH([=] { __llvm_libc::fputil::raiseExcept(e); }, + WITH_SIGNAL(SIGFPE)); + } +} diff --git a/libc/test/src/fenv/feupdateenv_test.cpp b/libc/test/src/fenv/feupdateenv_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/fenv/feupdateenv_test.cpp @@ -0,0 +1,27 @@ +//===-- Unittests for feupdateenv -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/fenv/feupdateenv.h" + +#include "utils/FPUtil/FEnv.h" +#include "utils/UnitTest/Test.h" + +#include +#include + +TEST(LlvmLibcFEnvTest, UpdateEnvTest) { + __llvm_libc::fputil::disableExcept(FE_ALL_EXCEPT); + __llvm_libc::fputil::clearExcept(FE_ALL_EXCEPT); + + fenv_t env; + ASSERT_EQ(__llvm_libc::fputil::getEnv(&env), 0); + __llvm_libc::fputil::setExcept(FE_INVALID | FE_INEXACT); + ASSERT_EQ(__llvm_libc::feupdateenv(&env), 0); + ASSERT_EQ(__llvm_libc::fputil::testExcept(FE_INVALID | FE_INEXACT), + FE_INVALID | FE_INEXACT); +} diff --git a/libc/test/src/fenv/getenv_and_setenv_test.cpp b/libc/test/src/fenv/getenv_and_setenv_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/fenv/getenv_and_setenv_test.cpp @@ -0,0 +1,39 @@ +//===-- Unittests for fegetenv and fesetenv -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/fenv/fegetenv.h" +#include "src/fenv/fesetenv.h" + +#include "utils/FPUtil/FEnv.h" +#include "utils/UnitTest/Test.h" + +#include + +TEST(LlvmLibcFenvTest, GetEnvAndSetEnv) { + // We will disable all exceptions to prevent invocation of the exception + // handler. + __llvm_libc::fputil::disableExcept(FE_ALL_EXCEPT); + + int excepts[] = {FE_DIVBYZERO, FE_INVALID, FE_INEXACT, FE_OVERFLOW, + FE_UNDERFLOW}; + + for (int e : excepts) { + __llvm_libc::fputil::clearExcept(FE_ALL_EXCEPT); + + // Save the cleared environment. + fenv_t env; + ASSERT_EQ(__llvm_libc::fegetenv(&env), 0); + + __llvm_libc::fputil::raiseExcept(e); + // Make sure that the exception is raised. + ASSERT_NE(__llvm_libc::fputil::testExcept(FE_ALL_EXCEPT) & e, 0); + + ASSERT_EQ(__llvm_libc::fesetenv(&env), 0); + ASSERT_EQ(__llvm_libc::fputil::testExcept(FE_ALL_EXCEPT) & e, 0); + } +} diff --git a/libc/utils/FPUtil/DummyFEnv.h b/libc/utils/FPUtil/DummyFEnv.h --- a/libc/utils/FPUtil/DummyFEnv.h +++ b/libc/utils/FPUtil/DummyFEnv.h @@ -21,12 +21,18 @@ static inline int testExcept(int) { return 0; } +static inline int setExcept(int) { return 0; } + static inline int raiseExcept(int) { return 0; } static inline int getRound() { return FE_TONEAREST; } static inline int setRound(int) { return 0; } +static inline int getEnv(fenv_t *) { return 0; } + +static inline int setEnv(const fenv_t *) { return 0; } + } // namespace fputil } // namespace __llvm_libc diff --git a/libc/utils/FPUtil/aarch64/FEnv.h b/libc/utils/FPUtil/aarch64/FEnv.h --- a/libc/utils/FPUtil/aarch64/FEnv.h +++ b/libc/utils/FPUtil/aarch64/FEnv.h @@ -19,6 +19,15 @@ namespace fputil { struct FEnv { + struct FPState { + uint32_t ControlWord; + uint32_t StatusWord; + }; + + static_assert( + sizeof(fenv_t) == sizeof(FPState), + "Internal floating point state does not match the public fenv_t type."); + static constexpr uint32_t ToNearest = 0x0; static constexpr uint32_t Upward = 0x1; static constexpr uint32_t Downward = 0x2; @@ -95,6 +104,14 @@ (statusWord >> FEnv::ExceptionStatusFlagsBitPosition) & toTest); } +static inline int setExcept(int excepts) { + uint32_t statusWord = FEnv::getControlWord(); + uint32_t statusValue = FEnv::getStatusValueForExcept(excepts); + statusWord |= (statusValue << FEnv::ExceptionStatusFlagsBitPosition); + FEnv::writeStatusWord(statusWord); + return 0; +} + static inline int raiseExcept(int excepts) { float zero = 0.0f; float one = 1.0f; @@ -198,6 +215,20 @@ return 0; } +static inline int getEnv(fenv_t *envp) { + FEnv::FPState *state = reinterpret_cast(envp); + state->ControlWord = FEnv::getControlWord(); + state->StatusWord = FEnv::getStatusWord(); + return 0; +} + +static inline int setEnv(const fenv_t *envp) { + const FEnv::FPState *state = reinterpret_cast(envp); + FEnv::writeControlWord(state->ControlWord); + FEnv::writeStatusWord(state->StatusWord); + return 0; +} + } // namespace fputil } // namespace __llvm_libc diff --git a/libc/utils/FPUtil/x86_64/FEnv.h b/libc/utils/FPUtil/x86_64/FEnv.h --- a/libc/utils/FPUtil/x86_64/FEnv.h +++ b/libc/utils/FPUtil/x86_64/FEnv.h @@ -74,7 +74,7 @@ (status & ExceptionFlags::Inexact ? FE_INEXACT : 0); } -struct X87State { +struct X87StateDescriptor { uint16_t ControlWord; uint16_t Unused1; uint16_t StatusWord; @@ -83,6 +83,15 @@ uint32_t _[5]; }; +struct FPState { + X87StateDescriptor X87Status; + uint32_t MXCSR; +}; + +static_assert( + sizeof(fenv_t) == sizeof(FPState), + "Internal floating point state does not match the public fenv_t type."); + static inline uint16_t getX87ControlWord() { uint16_t w; __asm__ __volatile__("fnstcw %0" : "=m"(w)::); @@ -113,11 +122,11 @@ __asm__ __volatile__("ldmxcsr %0" : : "m"(w) :); } -static inline void getX87State(X87State &s) { +static inline void getX87StateDescriptor(X87StateDescriptor &s) { __asm__ __volatile__("fnstenv %0" : "=m"(s)); } -static inline void writeX87State(const X87State &s) { +static inline void writeX87StateDescriptor(const X87StateDescriptor &s) { __asm__ __volatile__("fldenv %0" : : "m"(s) :); } @@ -194,6 +203,21 @@ (statusValue & internal::getMXCSR())); } +// Sets the exception flags but does not trigger the exception handler. +static inline int setExcept(int excepts) { + uint16_t statusValue = internal::getStatusValueForExcept(excepts); + internal::X87StateDescriptor state; + internal::getX87StateDescriptor(state); + state.StatusWord |= statusValue; + internal::writeX87StateDescriptor(state); + + uint32_t mxcsr = internal::getMXCSR(); + mxcsr |= statusValue; + internal::writeMXCSR(mxcsr); + + return 0; +} + static inline int raiseExcept(int excepts) { uint16_t statusValue = internal::getStatusValueForExcept(excepts); @@ -211,38 +235,38 @@ // when raising the next exception. if (statusValue & internal::ExceptionFlags::Invalid) { - internal::X87State state; - internal::getX87State(state); + internal::X87StateDescriptor state; + internal::getX87StateDescriptor(state); state.StatusWord |= internal::ExceptionFlags::Invalid; - internal::writeX87State(state); + internal::writeX87StateDescriptor(state); internal::fwait(); } if (statusValue & internal::ExceptionFlags::DivByZero) { - internal::X87State state; - internal::getX87State(state); + internal::X87StateDescriptor state; + internal::getX87StateDescriptor(state); state.StatusWord |= internal::ExceptionFlags::DivByZero; - internal::writeX87State(state); + internal::writeX87StateDescriptor(state); internal::fwait(); } if (statusValue & internal::ExceptionFlags::Overflow) { - internal::X87State state; - internal::getX87State(state); + internal::X87StateDescriptor state; + internal::getX87StateDescriptor(state); state.StatusWord |= internal::ExceptionFlags::Overflow; - internal::writeX87State(state); + internal::writeX87StateDescriptor(state); internal::fwait(); } if (statusValue & internal::ExceptionFlags::Underflow) { - internal::X87State state; - internal::getX87State(state); + internal::X87StateDescriptor state; + internal::getX87StateDescriptor(state); state.StatusWord |= internal::ExceptionFlags::Underflow; - internal::writeX87State(state); + internal::writeX87StateDescriptor(state); internal::fwait(); } if (statusValue & internal::ExceptionFlags::Inexact) { - internal::X87State state; - internal::getX87State(state); + internal::X87StateDescriptor state; + internal::getX87StateDescriptor(state); state.StatusWord |= internal::ExceptionFlags::Inexact; - internal::writeX87State(state); + internal::writeX87StateDescriptor(state); internal::fwait(); } @@ -309,6 +333,21 @@ return 0; } +static inline int getEnv(fenv_t *envp) { + internal::FPState *state = reinterpret_cast(envp); + internal::getX87StateDescriptor(state->X87Status); + state->MXCSR = internal::getMXCSR(); + return 0; +} + +static inline int setEnv(const fenv_t *envp) { + const internal::FPState *state = + reinterpret_cast(envp); + internal::writeX87StateDescriptor(state->X87Status); + internal::writeMXCSR(state->MXCSR); + return 0; +} + } // namespace fputil } // namespace __llvm_libc