[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