[libc-commits] [libc] 4fff2a7 - [libc] Add simple x86_64 floating point exception and rounding mode support.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Thu Dec 3 12:55:33 PST 2020


Author: Siva Chandra Reddy
Date: 2020-12-03T12:55:12-08:00
New Revision: 4fff2a7e8964574b05c77d76a8d63d1f3593a258

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

LOG: [libc] Add simple x86_64 floating point exception and rounding mode support.

Reviewed By: lntue

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

Added: 
    libc/include/fenv.h.def
    libc/src/fenv/CMakeLists.txt
    libc/src/fenv/feclearexcept.cpp
    libc/src/fenv/feclearexcept.h
    libc/src/fenv/fegetround.cpp
    libc/src/fenv/fegetround.h
    libc/src/fenv/feraiseexcept.cpp
    libc/src/fenv/feraiseexcept.h
    libc/src/fenv/fesetround.cpp
    libc/src/fenv/fesetround.h
    libc/src/fenv/fetestexcept.cpp
    libc/src/fenv/fetestexcept.h
    libc/test/src/fenv/CMakeLists.txt
    libc/test/src/fenv/exception_status_test.cpp
    libc/test/src/fenv/rounding_mode_test.cpp
    libc/utils/FPUtil/DummyFEnv.h
    libc/utils/FPUtil/FEnv.h
    libc/utils/FPUtil/x86_64/FEnv.h

Modified: 
    libc/config/linux/api.td
    libc/config/linux/x86_64/entrypoints.txt
    libc/include/CMakeLists.txt
    libc/spec/stdc.td
    libc/src/CMakeLists.txt
    libc/test/src/CMakeLists.txt
    libc/utils/FPUtil/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index d66d55f65027..72ca4e8bd349 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -179,6 +179,22 @@ def MathAPI : PublicAPI<"math.h"> {
   ];
 }
 
+def FenvAPI: PublicAPI<"fenv.h"> {
+  let Macros = [
+    SimpleMacroDef<"FE_DIVBYZERO", "1">,
+    SimpleMacroDef<"FE_INEXACT", "2">,
+    SimpleMacroDef<"FE_INVALID", "4">,
+    SimpleMacroDef<"FE_OVERFLOW", "8">,
+    SimpleMacroDef<"FE_UNDERFLOW", "16">,
+    SimpleMacroDef<"FE_ALL_EXCEPT", "(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW)">,
+
+    SimpleMacroDef<"FE_DOWNWARD", "1">,
+    SimpleMacroDef<"FE_TONEAREST", "2">,
+    SimpleMacroDef<"FE_TOWARDZERO", "4">,
+    SimpleMacroDef<"FE_UPWARD", "8">,
+  ];
+}
+
 def StringAPI : PublicAPI<"string.h"> {
   let TypeDeclarations = [
     SizeT,

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 7401715058ac..2b461c4eede5 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -21,6 +21,13 @@ set(TARGET_LIBC_ENTRYPOINTS
     # errno.h entrypoints
     libc.src.errno.__errno_location
 
+    # fenv.h entrypoints
+    libc.src.fenv.feclearexcept
+    libc.src.fenv.fegetround
+    libc.src.fenv.fesetround
+    libc.src.fenv.feraiseexcept
+    libc.src.fenv.fetestexcept
+
     # signal.h entrypoints
     libc.src.signal.raise
     libc.src.signal.sigaction

diff  --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index ddfdb8caa2ac..4c52a7615c91 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -25,6 +25,14 @@ add_gen_header(
     .llvm_libc_common_h
 )
 
+add_gen_header(
+  fenv
+  DEF_FILE fenv.h.def
+  GEN_HDR fenv.h
+  DEPENDS
+    .llvm_libc_common_h
+)
+
 add_gen_header(
   math
   DEF_FILE math.h.def

diff  --git a/libc/include/fenv.h.def b/libc/include/fenv.h.def
new file mode 100644
index 000000000000..489756cf6325
--- /dev/null
+++ b/libc/include/fenv.h.def
@@ -0,0 +1,16 @@
+//===-- C standard library header fenv.h ----------------------------------===//
+//
+// 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_FENV_H
+#define LLVM_LIBC_FENV_H
+
+#include <__llvm-libc-common.h>
+
+%%public_api()
+
+#endif // LLVM_LIBC_FENV_H

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index d40fe8df3942..70cc2600a612 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -95,7 +95,53 @@ def StdC : StandardSpec<"stdc"> {
           >,
       ]
   >;
-     
+
+  HeaderSpec Fenv = HeaderSpec<
+      "fenv.h",
+      [
+          Macro<"FE_DIVBYZERO">,
+          Macro<"FE_INEXACT">,
+          Macro<"FE_INVALID">,
+          Macro<"FE_OVERFLOW">,
+          Macro<"FE_UNDERFLOW">,
+          Macro<"FE_ALL_EXCEPT">,
+
+          Macro<"FE_DOWNWARD">,
+          Macro<"FE_TONEAREST">,
+          Macro<"FE_TOWARDZERO">,
+          Macro<"FE_UPWARD">
+      ],
+      [], // Types
+      [], // Enumerations
+      [
+          FunctionSpec<
+              "feclearexcept",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>]
+          >,
+          FunctionSpec<
+              "fetestexcept",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>]
+          >,
+          FunctionSpec<
+              "feraiseexcept",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>]
+          >,
+          FunctionSpec<
+              "fesetround",
+              RetValSpec<IntType>,
+              [ArgSpec<IntType>]
+          >,
+          FunctionSpec<
+              "fegetround",
+              RetValSpec<IntType>,
+              []
+          >,
+      ]
+  >;
+ 
   HeaderSpec String = HeaderSpec<
       "string.h",
       [
@@ -491,6 +537,7 @@ def StdC : StandardSpec<"stdc"> {
     Assert,
     CType,
     Errno,
+    Fenv,
     Math,
     String,
     StdIO,

diff  --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index 11213e90e579..8f389bce6d2a 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_subdirectory(assert)
 add_subdirectory(ctype)
 add_subdirectory(errno)
+add_subdirectory(fenv)
 add_subdirectory(math)
 add_subdirectory(signal)
 add_subdirectory(stdio)

diff  --git a/libc/src/fenv/CMakeLists.txt b/libc/src/fenv/CMakeLists.txt
new file mode 100644
index 000000000000..7044cc137e97
--- /dev/null
+++ b/libc/src/fenv/CMakeLists.txt
@@ -0,0 +1,64 @@
+add_entrypoint_object(
+  fegetround
+  SRCS
+    fegetround.cpp
+  HDRS
+    fegetround.h
+  DEPENDS
+    libc.include.fenv
+    libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  fesetround
+  SRCS
+    fesetround.cpp
+  HDRS
+    fesetround.h
+  DEPENDS
+    libc.include.fenv
+    libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  feclearexcept
+  SRCS
+    feclearexcept.cpp
+  HDRS
+    feclearexcept.h
+  DEPENDS
+    libc.include.fenv
+    libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  feraiseexcept
+  SRCS
+    feraiseexcept.cpp
+  HDRS
+    feraiseexcept.h
+  DEPENDS
+    libc.include.fenv
+    libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  fetestexcept
+  SRCS
+    fetestexcept.cpp
+  HDRS
+    fetestexcept.h
+  DEPENDS
+    libc.include.fenv
+    libc.utils.FPUtil.fputil
+  COMPILE_OPTIONS
+    -O2
+)

diff  --git a/libc/src/fenv/feclearexcept.cpp b/libc/src/fenv/feclearexcept.cpp
new file mode 100644
index 000000000000..c407ac48220a
--- /dev/null
+++ b/libc/src/fenv/feclearexcept.cpp
@@ -0,0 +1,18 @@
+//===-- Implementation of feclearexcept 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/__support/common.h"
+#include "utils/FPUtil/FEnv.h"
+
+namespace __llvm_libc {
+
+int LLVM_LIBC_ENTRYPOINT(feclearexcept)(int e) {
+  return fputil::clearExcept(e);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/fenv/feclearexcept.h b/libc/src/fenv/feclearexcept.h
new file mode 100644
index 000000000000..fa771cee32a1
--- /dev/null
+++ b/libc/src/fenv/feclearexcept.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for feclearexcept -----------------*- 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_FECLEAREXCEPT_H
+#define LLVM_LIBC_SRC_FENV_FECLEAREXCEPT_H
+
+namespace __llvm_libc {
+
+int feclearexcept(int);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_FENV_FECLEAREXCEPT_H

diff  --git a/libc/src/fenv/fegetround.cpp b/libc/src/fenv/fegetround.cpp
new file mode 100644
index 000000000000..0eb92243a15e
--- /dev/null
+++ b/libc/src/fenv/fegetround.cpp
@@ -0,0 +1,16 @@
+//===-- Implementation of fegetround 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/__support/common.h"
+#include "utils/FPUtil/FEnv.h"
+
+namespace __llvm_libc {
+
+int LLVM_LIBC_ENTRYPOINT(fegetround)() { return fputil::getRound(); }
+
+} // namespace __llvm_libc

diff  --git a/libc/src/fenv/fegetround.h b/libc/src/fenv/fegetround.h
new file mode 100644
index 000000000000..1bc79cbf5a6c
--- /dev/null
+++ b/libc/src/fenv/fegetround.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for fegetround --------------------*- 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_FEGETROUND_H
+#define LLVM_LIBC_SRC_FENV_FEGETROUND_H
+
+namespace __llvm_libc {
+
+int fegetround();
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_FENV_FEGETROUND_H

diff  --git a/libc/src/fenv/feraiseexcept.cpp b/libc/src/fenv/feraiseexcept.cpp
new file mode 100644
index 000000000000..a9e93e6a2ec4
--- /dev/null
+++ b/libc/src/fenv/feraiseexcept.cpp
@@ -0,0 +1,18 @@
+//===-- Implementation of feraiseexcept 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/__support/common.h"
+#include "utils/FPUtil/FEnv.h"
+
+namespace __llvm_libc {
+
+int LLVM_LIBC_ENTRYPOINT(feraiseexcept)(int e) {
+  return fputil::raiseExcept(e);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/fenv/feraiseexcept.h b/libc/src/fenv/feraiseexcept.h
new file mode 100644
index 000000000000..5c9eacf462d4
--- /dev/null
+++ b/libc/src/fenv/feraiseexcept.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for feraiseexcept -----------------*- 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_FERAISEEXCEPT_H
+#define LLVM_LIBC_SRC_FENV_FERAISEEXCEPT_H
+
+namespace __llvm_libc {
+
+int feraiseexcept(int);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_FENV_FERAISEEXCEPT_H

diff  --git a/libc/src/fenv/fesetround.cpp b/libc/src/fenv/fesetround.cpp
new file mode 100644
index 000000000000..3f6077d977d3
--- /dev/null
+++ b/libc/src/fenv/fesetround.cpp
@@ -0,0 +1,16 @@
+//===-- Implementation of fesetround 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/__support/common.h"
+#include "utils/FPUtil/FEnv.h"
+
+namespace __llvm_libc {
+
+int LLVM_LIBC_ENTRYPOINT(fesetround)(int m) { return fputil::setRound(m); }
+
+} // namespace __llvm_libc

diff  --git a/libc/src/fenv/fesetround.h b/libc/src/fenv/fesetround.h
new file mode 100644
index 000000000000..148a5eabcf3d
--- /dev/null
+++ b/libc/src/fenv/fesetround.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for fesetround --------------------*- 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_FESETROUND_H
+#define LLVM_LIBC_SRC_FENV_FESETROUND_H
+
+namespace __llvm_libc {
+
+int fesetround(int);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_FENV_FESETROUND_H

diff  --git a/libc/src/fenv/fetestexcept.cpp b/libc/src/fenv/fetestexcept.cpp
new file mode 100644
index 000000000000..38ec17b016f0
--- /dev/null
+++ b/libc/src/fenv/fetestexcept.cpp
@@ -0,0 +1,16 @@
+//===-- Implementation of fetestexcept 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/__support/common.h"
+#include "utils/FPUtil/FEnv.h"
+
+namespace __llvm_libc {
+
+int LLVM_LIBC_ENTRYPOINT(fetestexcept)(int e) { return fputil::testExcept(e); }
+
+} // namespace __llvm_libc

diff  --git a/libc/src/fenv/fetestexcept.h b/libc/src/fenv/fetestexcept.h
new file mode 100644
index 000000000000..fb3e4045dd6b
--- /dev/null
+++ b/libc/src/fenv/fetestexcept.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for fetestexpect ------------------*- 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_FETESTEXCEPT_H
+#define LLVM_LIBC_SRC_FENV_FETESTEXCEPT_H
+
+namespace __llvm_libc {
+
+int fetestexcept(int);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_FENV_FETESTEXCEPT_H

diff  --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index 026e7eab8aef..8375cd15bd84 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_subdirectory(assert)
 add_subdirectory(ctype)
 add_subdirectory(errno)
+add_subdirectory(fenv)
 add_subdirectory(math)
 add_subdirectory(signal)
 add_subdirectory(stdio)

diff  --git a/libc/test/src/fenv/CMakeLists.txt b/libc/test/src/fenv/CMakeLists.txt
new file mode 100644
index 000000000000..fb9666e2ed91
--- /dev/null
+++ b/libc/test/src/fenv/CMakeLists.txt
@@ -0,0 +1,24 @@
+add_libc_testsuite(libc_fenv_unittests)
+
+add_libc_unittest(
+  rounding_mode_test
+  SUITE
+    libc_fenv_unittests
+  SRCS
+    rounding_mode_test.cpp
+  DEPENDS
+    libc.src.fenv.fegetround
+    libc.src.fenv.fesetround
+)
+
+add_libc_unittest(
+  exception_status_test
+  SUITE
+    libc_fenv_unittests
+  SRCS
+    exception_status_test.cpp
+  DEPENDS
+    libc.src.fenv.feclearexcept
+    libc.src.fenv.feraiseexcept
+    libc.src.fenv.fetestexcept
+)

diff  --git a/libc/test/src/fenv/exception_status_test.cpp b/libc/test/src/fenv/exception_status_test.cpp
new file mode 100644
index 000000000000..353e5aa6b0e7
--- /dev/null
+++ b/libc/test/src/fenv/exception_status_test.cpp
@@ -0,0 +1,108 @@
+//===-- Unittests for feclearexcept, feraiseexcept and fetestexpect -------===//
+//
+// 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/feclearexcept.h"
+#include "src/fenv/feraiseexcept.h"
+#include "src/fenv/fetestexcept.h"
+
+#include "utils/UnitTest/Test.h"
+
+#include <fenv.h>
+
+TEST(ExceptionStatusTest, RaiseAndTest) {
+  int excepts[] = {FE_DIVBYZERO, FE_INVALID, FE_INEXACT, FE_OVERFLOW,
+                   FE_UNDERFLOW};
+  for (int e : excepts) {
+    int r = __llvm_libc::feraiseexcept(e);
+    EXPECT_EQ(r, 0);
+    int s = __llvm_libc::fetestexcept(e);
+    EXPECT_EQ(s, e);
+
+    r = __llvm_libc::feclearexcept(e);
+    EXPECT_EQ(r, 0);
+    s = __llvm_libc::fetestexcept(e);
+    EXPECT_EQ(s, 0);
+  }
+
+  for (int e1 : excepts) {
+    for (int e2 : excepts) {
+      int e = e1 | e2;
+      int r = __llvm_libc::feraiseexcept(e);
+      EXPECT_EQ(r, 0);
+      int s = __llvm_libc::fetestexcept(e);
+      EXPECT_EQ(s, e);
+
+      r = __llvm_libc::feclearexcept(e);
+      EXPECT_EQ(r, 0);
+      s = __llvm_libc::fetestexcept(e);
+      EXPECT_EQ(s, 0);
+    }
+  }
+
+  for (int e1 : excepts) {
+    for (int e2 : excepts) {
+      for (int e3 : excepts) {
+        int e = e1 | e2 | e3;
+        int r = __llvm_libc::feraiseexcept(e);
+        EXPECT_EQ(r, 0);
+        int s = __llvm_libc::fetestexcept(e);
+        EXPECT_EQ(s, e);
+
+        r = __llvm_libc::feclearexcept(e);
+        EXPECT_EQ(r, 0);
+        s = __llvm_libc::fetestexcept(e);
+        EXPECT_EQ(s, 0);
+      }
+    }
+  }
+
+  for (int e1 : excepts) {
+    for (int e2 : excepts) {
+      for (int e3 : excepts) {
+        for (int e4 : excepts) {
+          int e = e1 | e2 | e3 | e4;
+          int r = __llvm_libc::feraiseexcept(e);
+          EXPECT_EQ(r, 0);
+          int s = __llvm_libc::fetestexcept(e);
+          EXPECT_EQ(s, e);
+
+          r = __llvm_libc::feclearexcept(e);
+          EXPECT_EQ(r, 0);
+          s = __llvm_libc::fetestexcept(e);
+          EXPECT_EQ(s, 0);
+        }
+      }
+    }
+  }
+
+  for (int e1 : excepts) {
+    for (int e2 : excepts) {
+      for (int e3 : excepts) {
+        for (int e4 : excepts) {
+          for (int e5 : excepts) {
+            int e = e1 | e2 | e3 | e4 | e5;
+            int r = __llvm_libc::feraiseexcept(e);
+            EXPECT_EQ(r, 0);
+            int s = __llvm_libc::fetestexcept(e);
+            EXPECT_EQ(s, e);
+
+            r = __llvm_libc::feclearexcept(e);
+            EXPECT_EQ(r, 0);
+            s = __llvm_libc::fetestexcept(e);
+            EXPECT_EQ(s, 0);
+          }
+        }
+      }
+    }
+  }
+
+  int r = __llvm_libc::feraiseexcept(FE_ALL_EXCEPT);
+  EXPECT_EQ(r, 0);
+  int s = __llvm_libc::fetestexcept(FE_ALL_EXCEPT);
+  EXPECT_EQ(s, FE_ALL_EXCEPT);
+}

diff  --git a/libc/test/src/fenv/rounding_mode_test.cpp b/libc/test/src/fenv/rounding_mode_test.cpp
new file mode 100644
index 000000000000..73daf9db30bb
--- /dev/null
+++ b/libc/test/src/fenv/rounding_mode_test.cpp
@@ -0,0 +1,36 @@
+//===-- Unittests for fegetround and fesetround ---------------------------===//
+//
+// 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/fegetround.h"
+#include "src/fenv/fesetround.h"
+
+#include "utils/UnitTest/Test.h"
+
+#include <fenv.h>
+
+TEST(RoundingModeTest, SetAndGet) {
+  int s = __llvm_libc::fesetround(FE_TONEAREST);
+  EXPECT_EQ(s, 0);
+  int rm = __llvm_libc::fegetround();
+  EXPECT_EQ(rm, FE_TONEAREST);
+
+  s = __llvm_libc::fesetround(FE_UPWARD);
+  EXPECT_EQ(s, 0);
+  rm = __llvm_libc::fegetround();
+  EXPECT_EQ(rm, FE_UPWARD);
+
+  s = __llvm_libc::fesetround(FE_DOWNWARD);
+  EXPECT_EQ(s, 0);
+  rm = __llvm_libc::fegetround();
+  EXPECT_EQ(rm, FE_DOWNWARD);
+
+  s = __llvm_libc::fesetround(FE_TOWARDZERO);
+  EXPECT_EQ(s, 0);
+  rm = __llvm_libc::fegetround();
+  EXPECT_EQ(rm, FE_TOWARDZERO);
+}

diff  --git a/libc/utils/FPUtil/CMakeLists.txt b/libc/utils/FPUtil/CMakeLists.txt
index 1c90e017283a..a2d2aa18a953 100644
--- a/libc/utils/FPUtil/CMakeLists.txt
+++ b/libc/utils/FPUtil/CMakeLists.txt
@@ -4,14 +4,22 @@ else()
   set(LONG_DOUBLE_HDR)
 endif()
 
+if(EXISTS ${LIBC_TARGET_MACHINE})
+  set(FENV_IMPL ${LIBC_TARGET_MACHINE}/FEnv.h)
+else()
+  set(FENV_IMPL DummyFEnv.h)
+endif()
+
 add_header_library(
   fputil
   HDRS
     ${LONG_DOUBLE_HDR}
+    ${FENV_IMPL}
     BasicOperations.h
     BitPatterns.h
     ClassificationFunctions.h
     DivisionAndRemainderOperations.h
+    FEnv.h
     FloatOperations.h
     FloatProperties.h
     FPBits.h

diff  --git a/libc/utils/FPUtil/DummyFEnv.h b/libc/utils/FPUtil/DummyFEnv.h
new file mode 100644
index 000000000000..e303f6c2e690
--- /dev/null
+++ b/libc/utils/FPUtil/DummyFEnv.h
@@ -0,0 +1,32 @@
+//===-- Dummy floating point environment manipulation functins --*- 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_UTILS_FPUTIL_DUMMY_FENV_H
+#define LLVM_LIBC_UTILS_FPUTIL_DUMMY_FENV_H
+
+#include <math.h>
+
+namespace __llvm_libc {
+namespace fputil {
+
+// All dummy functions silently succeed.
+
+int clearExcept(int) { return 0; }
+
+int testExcept(int) { return 0; }
+
+int raiseExcept(int) { return 0; }
+
+int getRound() { return FE_TONEAREST; }
+
+int setRound(int) { return 0; }
+
+} // namespace fputil
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_UTILS_FPUTIL_DUMMY_FENV_H

diff  --git a/libc/utils/FPUtil/FEnv.h b/libc/utils/FPUtil/FEnv.h
new file mode 100644
index 000000000000..bf520672161e
--- /dev/null
+++ b/libc/utils/FPUtil/FEnv.h
@@ -0,0 +1,18 @@
+//===-- Utilities for manipulating floating point environment ---*- 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_UTILS_FPUTIL_FENV_H
+#define LLVM_LIBC_UTILS_FPUTIL_FENV_H
+
+#ifdef __x86_64__
+#include "x86_64/FEnv.h"
+#else
+#include "DummyFEnv.h"
+#endif
+
+#endif // LLVM_LIBC_UTILS_FPUTIL_FP_BITS_H

diff  --git a/libc/utils/FPUtil/x86_64/FEnv.h b/libc/utils/FPUtil/x86_64/FEnv.h
new file mode 100644
index 000000000000..63c9d41557cd
--- /dev/null
+++ b/libc/utils/FPUtil/x86_64/FEnv.h
@@ -0,0 +1,184 @@
+//===-- x86_64 floating point env manipulation functions --------*- 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_UTILS_FPUTIL_X86_64_FENV_H
+#define LLVM_LIBC_UTILS_FPUTIL_X86_64_FENV_H
+
+#include <fenv.h>
+#include <stdint.h>
+#include <xmmintrin.h>
+
+namespace __llvm_libc {
+namespace fputil {
+
+namespace internal {
+
+// Normally, one should be able to define FE_* macros to the exact rounding mode
+// encodings. However, since we want LLVM libc to be compiled against headers
+// from other libcs, we cannot assume that FE_* macros are always defined in
+// such a manner. So, we will define enums corresponding to the x86_64 bit
+// encodings. The implementations can map from FE_* to the corresponding enum
+// values.
+
+// The rounding control values in the x87 control register and the MXCSR
+// register have the same 2-bit enoding but have 
diff erent bit positions.
+// See below for the bit positions.
+struct RoundingControlValue {
+  static constexpr uint16_t ToNearest = 0x0;
+  static constexpr uint16_t Downward = 0x1;
+  static constexpr uint16_t Upward = 0x2;
+  static constexpr uint16_t TowardZero = 0x3;
+};
+
+static constexpr uint16_t X87RoundingControlBitPosition = 10;
+static constexpr uint16_t MXCSRRoundingControlBitPosition = 13;
+
+// The exception flags in the x87 status register and the MXCSR have the same
+// encoding as well as the same bit positions.
+struct ExceptionFlags {
+  static constexpr uint16_t Invalid = 0x1;
+  static constexpr uint16_t Denormal = 0x2; // This flag is not used
+  static constexpr uint16_t DivByZero = 0x4;
+  static constexpr uint16_t Overflow = 0x8;
+  static constexpr uint16_t Underflow = 0x10;
+  static constexpr uint16_t Inexact = 0x20;
+};
+
+// Exception flags are individual bits in the corresponding registers.
+// So, we just OR the bit values to get the full set of exceptions.
+static inline uint16_t getStatusValueForExcept(int excepts) {
+  // We will make use of the fact that exception control bits are single
+  // bit flags in the control registers.
+  return (excepts & FE_INVALID ? ExceptionFlags::Invalid : 0) |
+         (excepts & FE_DIVBYZERO ? ExceptionFlags::DivByZero : 0) |
+         (excepts & FE_OVERFLOW ? ExceptionFlags::Overflow : 0) |
+         (excepts & FE_UNDERFLOW ? ExceptionFlags::Underflow : 0) |
+         (excepts & FE_INEXACT ? ExceptionFlags::Inexact : 0);
+}
+
+static inline int exceptionStatusToMacro(uint16_t status) {
+  return (status & ExceptionFlags::Invalid ? FE_INVALID : 0) |
+         (status & ExceptionFlags::DivByZero ? FE_DIVBYZERO : 0) |
+         (status & ExceptionFlags::Overflow ? FE_OVERFLOW : 0) |
+         (status & ExceptionFlags::Underflow ? FE_UNDERFLOW : 0) |
+         (status & ExceptionFlags::Inexact ? FE_INEXACT : 0);
+}
+
+static inline uint16_t getX87ControlWord() {
+  uint16_t w;
+  __asm__ __volatile__("fnstcw %0" : "=m"(w)::);
+  return w;
+}
+
+static inline void writeX87ControlWord(uint16_t w) {
+  __asm__ __volatile__("fldcw %0" : : "m"(w) :);
+}
+
+static inline uint16_t getX87StatusWord() {
+  uint16_t w;
+  __asm__ __volatile__("fnstsw %0" : "=m"(w)::);
+  return w;
+}
+
+static inline void clearX87Exceptions() {
+  __asm__ __volatile__("fnclex" : : :);
+}
+
+} // namespace internal
+
+static inline int clearExcept(int excepts) {
+  // An instruction to write to x87 status word ins't available. So, we
+  // just clear all of the x87 exceptions.
+  // TODO: One can potentially use fegetenv/fesetenv to clear only the
+  // listed exceptions in the x87 status word. We can do this if it is
+  // really required.
+  internal::clearX87Exceptions();
+
+  uint32_t mxcsr = _mm_getcsr();
+  mxcsr &= ~internal::getStatusValueForExcept(excepts);
+  _mm_setcsr(mxcsr);
+  return 0;
+}
+
+static inline int testExcept(int excepts) {
+  uint16_t statusValue = internal::getStatusValueForExcept(excepts);
+  // Check both x87 status word and MXCSR.
+  return internal::exceptionStatusToMacro(
+      (statusValue & internal::getX87StatusWord()) |
+      (statusValue & _mm_getcsr()));
+}
+
+static inline int raiseExcept(int excepts) {
+  // It is enough to set the exception flags in MXCSR.
+  // TODO: Investigate if each exception has to be raised one at a time
+  // followed with an fwait instruction before writing the flag for the
+  // next exception.
+  uint16_t statusValue = internal::getStatusValueForExcept(excepts);
+  uint32_t sse = _mm_getcsr();
+  sse = sse | statusValue;
+  _mm_setcsr(sse);
+  return 0;
+}
+
+static inline int getRound() {
+  uint16_t bitValue =
+      (_mm_getcsr() >> internal::MXCSRRoundingControlBitPosition) & 0x3;
+  switch (bitValue) {
+  case internal::RoundingControlValue::ToNearest:
+    return FE_TONEAREST;
+  case internal::RoundingControlValue::Downward:
+    return FE_DOWNWARD;
+  case internal::RoundingControlValue::Upward:
+    return FE_UPWARD;
+  case internal::RoundingControlValue::TowardZero:
+    return FE_TOWARDZERO;
+  default:
+    return -1; // Error value.
+  }
+}
+
+static inline int setRound(int mode) {
+  uint16_t bitValue;
+  switch (mode) {
+  case FE_TONEAREST:
+    bitValue = internal::RoundingControlValue::ToNearest;
+    break;
+  case FE_DOWNWARD:
+    bitValue = internal::RoundingControlValue::Downward;
+    break;
+  case FE_UPWARD:
+    bitValue = internal::RoundingControlValue::Upward;
+    break;
+  case FE_TOWARDZERO:
+    bitValue = internal::RoundingControlValue::TowardZero;
+    break;
+  default:
+    return 1; // To indicate failure
+  }
+
+  uint16_t x87Value = bitValue << internal::X87RoundingControlBitPosition;
+  uint16_t x87Control = internal::getX87ControlWord();
+  x87Control =
+      (x87Control & ~(0x3 << internal::X87RoundingControlBitPosition)) |
+      x87Value;
+  internal::writeX87ControlWord(x87Control);
+
+  uint32_t mxcsrValue = bitValue << internal::MXCSRRoundingControlBitPosition;
+  uint32_t mxcsrControl = _mm_getcsr();
+  mxcsrControl =
+      (mxcsrControl & ~(0x3 << internal::MXCSRRoundingControlBitPosition)) |
+      mxcsrValue;
+  _mm_setcsr(mxcsrControl);
+
+  return 0;
+}
+
+} // namespace fputil
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_UTILS_FPUTIL_X86_64_FENV_H


        


More information about the libc-commits mailing list