[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