[libc-commits] [libc] b7e05c8 - [libc] Add implementations of the remaining fenv functions.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Thu Feb 18 13:29:50 PST 2021


Author: Siva Chandra Reddy
Date: 2021-02-18T13:29:40-08:00
New Revision: b7e05c874b5b2a77c87df71ee3442abc2569cbb9

URL: https://github.com/llvm/llvm-project/commit/b7e05c874b5b2a77c87df71ee3442abc2569cbb9
DIFF: https://github.com/llvm/llvm-project/commit/b7e05c874b5b2a77c87df71ee3442abc2569cbb9.diff

LOG: [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

Added: 
    libc/src/fenv/fegetenv.cpp
    libc/src/fenv/fegetenv.h
    libc/src/fenv/fegetexceptflag.cpp
    libc/src/fenv/fegetexceptflag.h
    libc/src/fenv/feholdexcept.cpp
    libc/src/fenv/feholdexcept.h
    libc/src/fenv/fesetenv.cpp
    libc/src/fenv/fesetenv.h
    libc/src/fenv/fesetexceptflag.cpp
    libc/src/fenv/fesetexceptflag.h
    libc/src/fenv/feupdateenv.cpp
    libc/src/fenv/feupdateenv.h
    libc/test/src/fenv/exception_flags_test.cpp
    libc/test/src/fenv/feholdexcept_test.cpp
    libc/test/src/fenv/feupdateenv_test.cpp
    libc/test/src/fenv/getenv_and_setenv_test.cpp

Modified: 
    libc/config/linux/aarch64/entrypoints.txt
    libc/config/linux/api.td
    libc/config/linux/x86_64/entrypoints.txt
    libc/spec/stdc.td
    libc/src/fenv/CMakeLists.txt
    libc/test/src/fenv/CMakeLists.txt
    libc/utils/FPUtil/DummyFEnv.h
    libc/utils/FPUtil/aarch64/FEnv.h
    libc/utils/FPUtil/x86_64/FEnv.h

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 17cbd84078fa..b90ae1774bdc 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 72ca4e8bd349..fa7db68466de 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 d073e63d715f..0db3e148923a 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 e89d16633ae3..9b332a8c1bbc 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<FEnvT>;
+  ConstType ConstFEnvTPtr = ConstType<FEnvTPtr>;
+  NamedType FExceptT = NamedType<"fexcept_t">;
+  PtrType FExceptTPtr = PtrType<FExceptT>;
+  ConstType ConstFExceptTPtr = ConstType<FExceptTPtr>;
   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<IntType>,
               []
           >,
+          FunctionSpec<
+              "fegetenv",
+              RetValSpec<IntType>,
+              [ArgSpec<FEnvTPtr>]
+          >,
+          FunctionSpec<
+              "fesetenv",
+              RetValSpec<IntType>,
+              [ArgSpec<ConstFEnvTPtr>]
+          >,
+          FunctionSpec<
+              "fegetexceptflag",
+              RetValSpec<IntType>,
+              [ArgSpec<FExceptTPtr>, ArgSpec<IntType>]
+          >,
+          FunctionSpec<
+              "fesetexceptflag",
+              RetValSpec<IntType>,
+              [ArgSpec<ConstFExceptTPtr>, ArgSpec<IntType>]
+          >,
+          FunctionSpec<
+              "feholdexcept",
+              RetValSpec<IntType>,
+              [ArgSpec<FEnvTPtr>]
+          >,
+          FunctionSpec<
+              "feupdateenv",
+              RetValSpec<IntType>,
+              [ArgSpec<ConstFEnvTPtr>]
+          >,
       ]
   >;
  

diff  --git a/libc/src/fenv/CMakeLists.txt b/libc/src/fenv/CMakeLists.txt
index 7044cc137e97..be1adf1bb98c 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 000000000000..28fef8a699af
--- /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 000000000000..e1001682a42b
--- /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 <fenv.h>
+
+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 000000000000..06ca56e55ed4
--- /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 <fenv.h>
+
+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<int *>(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 000000000000..20913cb7a22f
--- /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 <fenv.h>
+
+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 000000000000..76a82d2800eb
--- /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 <fenv.h>
+
+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 000000000000..cfb86b54ff49
--- /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 <fenv.h>
+
+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 000000000000..5f6ede84da9c
--- /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 000000000000..316ecee41996
--- /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 <fenv.h>
+
+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 000000000000..3c88d63dffbe
--- /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 <fenv.h>
+
+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<const int *>(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 000000000000..0e6497abafae
--- /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 <fenv.h>
+
+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 000000000000..ff45991d4ce7
--- /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 <fenv.h>
+
+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 000000000000..1599c01ebddf
--- /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 <fenv.h>
+
+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 09e37b15a7f2..851db0398569 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 000000000000..e492a21c1b1e
--- /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 <fenv.h>
+
+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 000000000000..2517b3ebbcb2
--- /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 <fenv.h>
+#include <signal.h>
+
+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 000000000000..da40ab18622a
--- /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 <fenv.h>
+#include <signal.h>
+
+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 000000000000..ebd6d47707bd
--- /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 <fenv.h>
+
+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 19c661fd23eb..216749fc538d 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 ac0ef70b522d..44723ace3f3e 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<FEnv::FPState *>(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<const FEnv::FPState *>(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 08379fd446da..3fb4881c5f2c 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<internal::FPState *>(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<const internal::FPState *>(envp);
+  internal::writeX87StateDescriptor(state->X87Status);
+  internal::writeMXCSR(state->MXCSR);
+  return 0;
+}
+
 } // namespace fputil
 } // namespace __llvm_libc
 


        


More information about the libc-commits mailing list