From b7e05c874b5b2a77c87df71ee3442abc2569cbb9 Mon Sep 17 00:00:00 2001 From: Siva Chandra Reddy Date: Mon, 15 Feb 2021 22:36:55 -0800 Subject: [PATCH] [libc] Add implementations of the remaining fenv functions. Namely, implementations of fegetexceptfflag, fesetexceptflag, fegetenv, fesetenv, feholdexcept and feupdateenv have been added. Reviewed By: lntue Differential Revision: https://reviews.llvm.org/D96935 --- libc/config/linux/aarch64/entrypoints.txt | 6 ++ libc/config/linux/api.td | 27 +++++++ libc/config/linux/x86_64/entrypoints.txt | 6 ++ libc/spec/stdc.td | 41 +++++++++- libc/src/fenv/CMakeLists.txt | 78 +++++++++++++++++++ libc/src/fenv/fegetenv.cpp | 19 +++++ libc/src/fenv/fegetenv.h | 20 +++++ libc/src/fenv/fegetexceptflag.cpp | 26 +++++++ libc/src/fenv/fegetexceptflag.h | 20 +++++ libc/src/fenv/feholdexcept.cpp | 25 ++++++ libc/src/fenv/feholdexcept.h | 20 +++++ libc/src/fenv/fesetenv.cpp | 19 +++++ libc/src/fenv/fesetenv.h | 20 +++++ libc/src/fenv/fesetexceptflag.cpp | 27 +++++++ libc/src/fenv/fesetexceptflag.h | 20 +++++ libc/src/fenv/feupdateenv.cpp | 24 ++++++ libc/src/fenv/feupdateenv.h | 20 +++++ libc/test/src/fenv/CMakeLists.txt | 48 ++++++++++++ libc/test/src/fenv/exception_flags_test.cpp | 45 +++++++++++ libc/test/src/fenv/feholdexcept_test.cpp | 37 +++++++++ libc/test/src/fenv/feupdateenv_test.cpp | 27 +++++++ libc/test/src/fenv/getenv_and_setenv_test.cpp | 39 ++++++++++ libc/utils/FPUtil/DummyFEnv.h | 6 ++ libc/utils/FPUtil/aarch64/FEnv.h | 31 ++++++++ libc/utils/FPUtil/x86_64/FEnv.h | 75 +++++++++++++----- 25 files changed, 707 insertions(+), 19 deletions(-) create mode 100644 libc/src/fenv/fegetenv.cpp create mode 100644 libc/src/fenv/fegetenv.h create mode 100644 libc/src/fenv/fegetexceptflag.cpp create mode 100644 libc/src/fenv/fegetexceptflag.h create mode 100644 libc/src/fenv/feholdexcept.cpp create mode 100644 libc/src/fenv/feholdexcept.h create mode 100644 libc/src/fenv/fesetenv.cpp create mode 100644 libc/src/fenv/fesetenv.h create mode 100644 libc/src/fenv/fesetexceptflag.cpp create mode 100644 libc/src/fenv/fesetexceptflag.h create mode 100644 libc/src/fenv/feupdateenv.cpp create mode 100644 libc/src/fenv/feupdateenv.h create mode 100644 libc/test/src/fenv/exception_flags_test.cpp create mode 100644 libc/test/src/fenv/feholdexcept_test.cpp create mode 100644 libc/test/src/fenv/feupdateenv_test.cpp create mode 100644 libc/test/src/fenv/getenv_and_setenv_test.cpp diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 17cbd84078fa4b..b90ae1774bdca6 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -22,10 +22,16 @@ set(TARGET_LIBC_ENTRYPOINTS # 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 index 72ca4e8bd349a1..fa7db68466de4b 100644 --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -179,6 +179,29 @@ def MathAPI : PublicAPI<"math.h"> { ]; } +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 @@ def FenvAPI: PublicAPI<"fenv.h"> { 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 index d073e63d715f63..0db3e148923a50 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -25,10 +25,16 @@ set(TARGET_LIBC_ENTRYPOINTS # 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 index e89d16633ae31f..9b332a8c1bbc2e 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -96,6 +96,12 @@ def StdC : StandardSpec<"stdc"> { ] >; + 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 @@ def StdC : StandardSpec<"stdc"> { Macro<"FE_TOWARDZERO">, Macro<"FE_UPWARD"> ], - [], // Types + [ + NamedType<"fenv_t">, + NamedType<"fexcept_t">, + ], // Types [], // Enumerations [ FunctionSpec< @@ -139,6 +148,36 @@ def StdC : StandardSpec<"stdc"> { 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 index 7044cc137e97b3..be1adf1bb98c53 100644 --- a/libc/src/fenv/CMakeLists.txt +++ b/libc/src/fenv/CMakeLists.txt @@ -62,3 +62,81 @@ add_entrypoint_object( 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.cpp b/libc/src/fenv/fegetenv.cpp new file mode 100644 index 00000000000000..28fef8a699af29 --- /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/fegetenv.h b/libc/src/fenv/fegetenv.h new file mode 100644 index 00000000000000..e1001682a42bba --- /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/fegetexceptflag.cpp b/libc/src/fenv/fegetexceptflag.cpp new file mode 100644 index 00000000000000..06ca56e55ed408 --- /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/fegetexceptflag.h b/libc/src/fenv/fegetexceptflag.h new file mode 100644 index 00000000000000..20913cb7a22f84 --- /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/feholdexcept.cpp b/libc/src/fenv/feholdexcept.cpp new file mode 100644 index 00000000000000..76a82d2800ebd1 --- /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/feholdexcept.h b/libc/src/fenv/feholdexcept.h new file mode 100644 index 00000000000000..cfb86b54ff49e9 --- /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/fesetenv.cpp b/libc/src/fenv/fesetenv.cpp new file mode 100644 index 00000000000000..5f6ede84da9cba --- /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/fesetenv.h b/libc/src/fenv/fesetenv.h new file mode 100644 index 00000000000000..316ecee41996e1 --- /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/fesetexceptflag.cpp b/libc/src/fenv/fesetexceptflag.cpp new file mode 100644 index 00000000000000..3c88d63dffbe03 --- /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/fesetexceptflag.h b/libc/src/fenv/fesetexceptflag.h new file mode 100644 index 00000000000000..0e6497abafaed4 --- /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/feupdateenv.cpp b/libc/src/fenv/feupdateenv.cpp new file mode 100644 index 00000000000000..ff45991d4ce702 --- /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/src/fenv/feupdateenv.h b/libc/src/fenv/feupdateenv.h new file mode 100644 index 00000000000000..1599c01ebddffa --- /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/test/src/fenv/CMakeLists.txt b/libc/test/src/fenv/CMakeLists.txt index 09e37b15a7f2d0..851db03985698c 100644 --- a/libc/test/src/fenv/CMakeLists.txt +++ b/libc/test/src/fenv/CMakeLists.txt @@ -24,6 +24,42 @@ add_libc_unittest( 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 @@ if (NOT LLVM_USE_SANITIZER) 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 index 00000000000000..e492a21c1b1e2d --- /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 index 00000000000000..2517b3ebbcb208 --- /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 index 00000000000000..da40ab18622a92 --- /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 index 00000000000000..ebd6d47707bdb3 --- /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 index 19c661fd23eb18..216749fc538d4b 100644 --- a/libc/utils/FPUtil/DummyFEnv.h +++ b/libc/utils/FPUtil/DummyFEnv.h @@ -21,12 +21,18 @@ static inline int clearExcept(int) { return 0; } 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 index ac0ef70b522da5..44723ace3f3e64 100644 --- a/libc/utils/FPUtil/aarch64/FEnv.h +++ b/libc/utils/FPUtil/aarch64/FEnv.h @@ -19,6 +19,15 @@ namespace __llvm_libc { 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 @@ static inline int testExcept(int excepts) { (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 @@ static inline int setRound(int mode) { 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 index 08379fd446da15..3fb4881c5f2caa 100644 --- a/libc/utils/FPUtil/x86_64/FEnv.h +++ b/libc/utils/FPUtil/x86_64/FEnv.h @@ -74,7 +74,7 @@ static inline int exceptionStatusToMacro(uint16_t status) { (status & ExceptionFlags::Inexact ? FE_INEXACT : 0); } -struct X87State { +struct X87StateDescriptor { uint16_t ControlWord; uint16_t Unused1; uint16_t StatusWord; @@ -83,6 +83,15 @@ struct X87State { 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 @@ static inline void writeMXCSR(uint32_t w) { __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 @@ static inline int testExcept(int excepts) { (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 @@ static inline int raiseExcept(int excepts) { // 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 @@ static inline int setRound(int mode) { 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