[libc] Add extension functions fedisableexcept, feenableexcept and fegetexcept.

Reviewed By: michaelrj

Differential Revision: https://reviews.llvm.org/D109613

GitOrigin-RevId: 0da5ac1a7537f05d6ca87ec2e76b25f3b555c301
diff --git a/config/linux/aarch64/entrypoints.txt b/config/linux/aarch64/entrypoints.txt
index f834767..8dbbb64 100644
--- a/config/linux/aarch64/entrypoints.txt
+++ b/config/linux/aarch64/entrypoints.txt
@@ -62,7 +62,10 @@
 set(TARGET_LIBM_ENTRYPOINTS
     # fenv.h entrypoints
     libc.src.fenv.feclearexcept
+    libc.src.fenv.fedisableexcept
+    libc.src.fenv.feenableexcept
     libc.src.fenv.fegetenv
+    libc.src.fenv.fegetexcept
     libc.src.fenv.fegetexceptflag
     libc.src.fenv.fegetround
     libc.src.fenv.feholdexcept
diff --git a/config/linux/x86_64/entrypoints.txt b/config/linux/x86_64/entrypoints.txt
index 59c5f51..9836d62 100644
--- a/config/linux/x86_64/entrypoints.txt
+++ b/config/linux/x86_64/entrypoints.txt
@@ -62,7 +62,10 @@
 set(TARGET_LIBM_ENTRYPOINTS
     # fenv.h entrypoints
     libc.src.fenv.feclearexcept
+    libc.src.fenv.fedisableexcept
+    libc.src.fenv.feenableexcept
     libc.src.fenv.fegetenv
+    libc.src.fenv.fegetexcept
     libc.src.fenv.fegetexceptflag
     libc.src.fenv.fegetround
     libc.src.fenv.feholdexcept
diff --git a/spec/gnu_ext.td b/spec/gnu_ext.td
index 0b0b8ca..99b78de 100644
--- a/spec/gnu_ext.td
+++ b/spec/gnu_ext.td
@@ -41,9 +41,33 @@
       ]
   >;
 
+  HeaderSpec FEnv = HeaderSpec<
+      "fenv.h",
+      [], // Macros
+      [], // Types
+      [], // Enumerations
+      [
+        FunctionSpec<
+            "fedisableexcept",
+            RetValSpec<IntType>,
+            [ArgSpec<IntType>]
+        >,
+        FunctionSpec<
+            "feenableexcept",
+            RetValSpec<IntType>,
+            [ArgSpec<IntType>]
+        >,
+        FunctionSpec<
+            "fegetexcept",
+            RetValSpec<IntType>,
+            []
+        >
+      ]
+  >;
 
   let Headers = [
     CType,
+    FEnv,
     Math, 
     String,
   ];
diff --git a/src/fenv/CMakeLists.txt b/src/fenv/CMakeLists.txt
index ea3946b..2a210c0 100644
--- a/src/fenv/CMakeLists.txt
+++ b/src/fenv/CMakeLists.txt
@@ -140,3 +140,42 @@
   COMPILE_OPTIONS
     -O2
 )
+
+add_entrypoint_object(
+  feenableexcept
+  SRCS
+    feenableexcept.cpp
+  HDRS
+    feenableexcept.h
+  DEPENDS
+    libc.include.fenv
+    libc.src.__support.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  fedisableexcept
+  SRCS
+    fedisableexcept.cpp
+  HDRS
+    fedisableexcept.h
+  DEPENDS
+    libc.include.fenv
+    libc.src.__support.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  fegetexcept
+  SRCS
+    fegetexcept.cpp
+  HDRS
+    fegetexcept.h
+  DEPENDS
+    libc.include.fenv
+    libc.src.__support.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
+)
diff --git a/src/fenv/fedisableexcept.cpp b/src/fenv/fedisableexcept.cpp
new file mode 100644
index 0000000..a31bb94
--- /dev/null
+++ b/src/fenv/fedisableexcept.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of fedisableexcept 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/fedisableexcept.h"
+#include "src/__support/FPUtil/FEnvUtils.h"
+#include "src/__support/common.h"
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, fedisableexcept, (int e)) {
+  return fputil::disableExcept(e);
+}
+
+} // namespace __llvm_libc
diff --git a/src/fenv/fedisableexcept.h b/src/fenv/fedisableexcept.h
new file mode 100644
index 0000000..44a545e
--- /dev/null
+++ b/src/fenv/fedisableexcept.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for fedisableexcept ---------------*- 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_FEDISABLEEXCEPT_H
+#define LLVM_LIBC_SRC_FENV_FEDISABLEEXCEPT_H
+
+namespace __llvm_libc {
+
+int fedisableexcept(int);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_FENV_FEDISABLEEXCEPT_H
diff --git a/src/fenv/feenableexcept.cpp b/src/fenv/feenableexcept.cpp
new file mode 100644
index 0000000..1046bab
--- /dev/null
+++ b/src/fenv/feenableexcept.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of feenableexcept 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/feenableexcept.h"
+#include "src/__support/FPUtil/FEnvUtils.h"
+#include "src/__support/common.h"
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, feenableexcept, (int e)) {
+  return fputil::enableExcept(e);
+}
+
+} // namespace __llvm_libc
diff --git a/src/fenv/feenableexcept.h b/src/fenv/feenableexcept.h
new file mode 100644
index 0000000..58c1e0a
--- /dev/null
+++ b/src/fenv/feenableexcept.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for feenableexcept ----------------*- 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_FEENABLEEXCEPT_H
+#define LLVM_LIBC_SRC_FENV_FEENABLEEXCEPT_H
+
+namespace __llvm_libc {
+
+int feenableexcept(int);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_FENV_FEENABLEEXCEPT_H
diff --git a/src/fenv/fegetexcept.cpp b/src/fenv/fegetexcept.cpp
new file mode 100644
index 0000000..1c9ec37
--- /dev/null
+++ b/src/fenv/fegetexcept.cpp
@@ -0,0 +1,17 @@
+//===-- Implementation of fegetexcept 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/fegetexcept.h"
+#include "src/__support/FPUtil/FEnvUtils.h"
+#include "src/__support/common.h"
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, fegetexcept, ()) { return fputil::getExcept(); }
+
+} // namespace __llvm_libc
diff --git a/src/fenv/fegetexcept.h b/src/fenv/fegetexcept.h
new file mode 100644
index 0000000..ed6579f
--- /dev/null
+++ b/src/fenv/fegetexcept.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for fegetexcept -------------------*- 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_FEGETEXCEPT_H
+#define LLVM_LIBC_SRC_FENV_FEGETEXCEPT_H
+
+namespace __llvm_libc {
+
+int fegetexcept();
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_FENV_FEGETEXCEPT_H
diff --git a/test/src/fenv/CMakeLists.txt b/test/src/fenv/CMakeLists.txt
index 64f43fa..bab83c1 100644
--- a/test/src/fenv/CMakeLists.txt
+++ b/test/src/fenv/CMakeLists.txt
@@ -71,6 +71,18 @@
     libc.src.__support.FPUtil.fputil
 )
 
+add_libc_unittest(
+  feenableexcept_test
+  SUITE
+    libc_fenv_unittests
+  SRCS
+    feenableexcept_test.cpp
+  DEPENDS
+    libc.src.fenv.fedisableexcept
+    libc.src.fenv.feenableexcept
+    libc.src.fenv.fegetexcept
+)
+
 if (NOT (LLVM_USE_SANITIZER OR (${LIBC_TARGET_OS} STREQUAL "windows")))
   # Sanitizers don't like SIGFPE. So, we will run the 
   # tests which raise SIGFPE only in non-sanitizer builds.
diff --git a/test/src/fenv/feenableexcept_test.cpp b/test/src/fenv/feenableexcept_test.cpp
new file mode 100644
index 0000000..2158f95
--- /dev/null
+++ b/test/src/fenv/feenableexcept_test.cpp
@@ -0,0 +1,86 @@
+//===-- Unittests for feenableexcept  -------------------------------------===//
+//
+// 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/fedisableexcept.h"
+#include "src/fenv/feenableexcept.h"
+#include "src/fenv/fegetexcept.h"
+
+#include "utils/UnitTest/Test.h"
+
+#include <fenv.h>
+
+TEST(LlvmLibcFEnvTest, EnableTest) {
+#ifdef __aarch64__
+  // Few aarch64 HW implementations do not trap exceptions. We skip this test
+  // completely on such HW.
+  //
+  // Whether HW supports trapping exceptions or not is deduced by enabling an
+  // exception and reading back to see if the exception got enabled. If the
+  // exception did not get enabled, then it means that the HW does not support
+  // trapping exceptions.
+  __llvm_libc::fedisableexcept(FE_ALL_EXCEPT);
+  __llvm_libc::feenableexcept(FE_DIVBYZERO);
+  if (__llvm_libc::fegetexcept() == 0)
+    return;
+#endif
+
+  int excepts[] = {FE_DIVBYZERO, FE_INVALID, FE_INEXACT, FE_OVERFLOW,
+                   FE_UNDERFLOW};
+  __llvm_libc::fedisableexcept(FE_ALL_EXCEPT);
+  ASSERT_EQ(0, __llvm_libc::fegetexcept());
+
+  for (int e : excepts) {
+    __llvm_libc::feenableexcept(e);
+    ASSERT_EQ(e, __llvm_libc::fegetexcept());
+    __llvm_libc::fedisableexcept(e);
+  }
+
+  for (int e1 : excepts) {
+    for (int e2 : excepts) {
+      __llvm_libc::feenableexcept(e1 | e2);
+      ASSERT_EQ(e1 | e2, __llvm_libc::fegetexcept());
+      __llvm_libc::fedisableexcept(e1 | e2);
+    }
+  }
+
+  for (int e1 : excepts) {
+    for (int e2 : excepts) {
+      for (int e3 : excepts) {
+        __llvm_libc::feenableexcept(e1 | e2 | e3);
+        ASSERT_EQ(e1 | e2 | e3, __llvm_libc::fegetexcept());
+        __llvm_libc::fedisableexcept(e1 | e2 | e3);
+      }
+    }
+  }
+
+  for (int e1 : excepts) {
+    for (int e2 : excepts) {
+      for (int e3 : excepts) {
+        for (int e4 : excepts) {
+          __llvm_libc::feenableexcept(e1 | e2 | e3 | e4);
+          ASSERT_EQ(e1 | e2 | e3 | e4, __llvm_libc::fegetexcept());
+          __llvm_libc::fedisableexcept(e1 | e2 | e3 | e4);
+        }
+      }
+    }
+  }
+
+  for (int e1 : excepts) {
+    for (int e2 : excepts) {
+      for (int e3 : excepts) {
+        for (int e4 : excepts) {
+          for (int e5 : excepts) {
+            __llvm_libc::feenableexcept(e1 | e2 | e3 | e4 | e5);
+            ASSERT_EQ(e1 | e2 | e3 | e4 | e5, __llvm_libc::fegetexcept());
+            __llvm_libc::fedisableexcept(e1 | e2 | e3 | e4 | e5);
+          }
+        }
+      }
+    }
+  }
+}