forked from OSchip/llvm-project
[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
This commit is contained in:
parent
53e83afcaf
commit
b7e05c874b
|
@ -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
|
||||
|
|
|
@ -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"> {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>]
|
||||
>,
|
||||
]
|
||||
>;
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue