[libc] Add FE_DFL_ENV and handle it in fesetenv.
Reviewed By: michaelrj Differential Revision: https://reviews.llvm.org/D110611
This commit is contained in:
parent
113fa82c3c
commit
5c3c716bb1
|
@ -230,6 +230,8 @@ def FenvAPI: PublicAPI<"fenv.h"> {
|
|||
SimpleMacroDef<"FE_TONEAREST", "2">,
|
||||
SimpleMacroDef<"FE_TOWARDZERO", "4">,
|
||||
SimpleMacroDef<"FE_UPWARD", "8">,
|
||||
|
||||
SimpleMacroDef<"FE_DFL_ENV", "((fenv_t *)-1)">,
|
||||
];
|
||||
let TypeDeclarations = [
|
||||
FEnvT,
|
||||
|
|
|
@ -120,7 +120,9 @@ def StdC : StandardSpec<"stdc"> {
|
|||
Macro<"FE_DOWNWARD">,
|
||||
Macro<"FE_TONEAREST">,
|
||||
Macro<"FE_TOWARDZERO">,
|
||||
Macro<"FE_UPWARD">
|
||||
Macro<"FE_UPWARD">,
|
||||
|
||||
Macro<"FE_DFL_ENV">
|
||||
],
|
||||
[
|
||||
NamedType<"fenv_t">,
|
||||
|
|
|
@ -230,6 +230,13 @@ static inline int getEnv(fenv_t *envp) {
|
|||
}
|
||||
|
||||
static inline int setEnv(const fenv_t *envp) {
|
||||
if (envp == FE_DFL_ENV) {
|
||||
// Default status and control words bits are all zeros so we just
|
||||
// write zeros.
|
||||
FEnv::writeStatusWord(0);
|
||||
FEnv::writeControlWord(0);
|
||||
return 0;
|
||||
}
|
||||
const FEnv::FPState *state = reinterpret_cast<const FEnv::FPState *>(envp);
|
||||
FEnv::writeControlWord(state->ControlWord);
|
||||
FEnv::writeStatusWord(state->StatusWord);
|
||||
|
|
|
@ -381,10 +381,58 @@ static inline int getEnv(fenv_t *envp) {
|
|||
}
|
||||
|
||||
static inline int setEnv(const fenv_t *envp) {
|
||||
const internal::FPState *state =
|
||||
// envp contains everything including pieces like the current
|
||||
// top of FPU stack. We cannot arbitrarily change them. So, we first
|
||||
// read the current status and update only those pieces which are
|
||||
// not disruptive.
|
||||
internal::X87StateDescriptor x87Status;
|
||||
internal::getX87StateDescriptor(x87Status);
|
||||
|
||||
if (envp == FE_DFL_ENV) {
|
||||
// Reset the exception flags in the status word.
|
||||
x87Status.StatusWord &= ~uint16_t(0x3F);
|
||||
// Reset other non-sensitive parts of the status word.
|
||||
for (int i = 0; i < 5; i++)
|
||||
x87Status._[i] = 0;
|
||||
// In the control word, we do the following:
|
||||
// 1. Mask all exceptions
|
||||
// 2. Set rounding mode to round-to-nearest
|
||||
// 3. Set the internal precision to double extended precision.
|
||||
x87Status.ControlWord |= uint16_t(0x3F); // Mask all exceptions.
|
||||
x87Status.ControlWord &= ~(uint16_t(0x3) << 10); // Round to nearest.
|
||||
x87Status.ControlWord |= (uint16_t(0x3) << 8); // Extended precision.
|
||||
internal::writeX87StateDescriptor(x87Status);
|
||||
|
||||
// We take the exact same approach MXCSR register as well.
|
||||
// MXCSR has two additional fields, "flush-to-zero" and
|
||||
// "denormals-are-zero". We reset those bits. Also, MXCSR does not
|
||||
// have a field which controls the precision of internal operations.
|
||||
uint32_t mxcsr = internal::getMXCSR();
|
||||
mxcsr &= ~uint16_t(0x3F); // Clear exception flags.
|
||||
mxcsr &= ~(uint16_t(0x1) << 6); // Reset denormals-are-zero
|
||||
mxcsr |= (uint16_t(0x3F) << 7); // Mask exceptions
|
||||
mxcsr &= ~(uint16_t(0x3) << 13); // Round to nearest.
|
||||
mxcsr &= ~(uint16_t(0x1) << 15); // Reset flush-to-zero
|
||||
internal::writeMXCSR(mxcsr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const internal::FPState *fpstate =
|
||||
reinterpret_cast<const internal::FPState *>(envp);
|
||||
internal::writeX87StateDescriptor(state->X87Status);
|
||||
internal::writeMXCSR(state->MXCSR);
|
||||
|
||||
// Copy the exception status flags from envp.
|
||||
x87Status.StatusWord &= ~uint16_t(0x3F);
|
||||
x87Status.StatusWord |= (fpstate->X87Status.StatusWord & 0x3F);
|
||||
// Copy other non-sensitive parts of the status word.
|
||||
for (int i = 0; i < 5; i++)
|
||||
x87Status._[i] = fpstate->X87Status._[i];
|
||||
// We can set the x87 control word as is as there no sensitive bits.
|
||||
x87Status.ControlWord = fpstate->X87Status.ControlWord;
|
||||
internal::writeX87StateDescriptor(x87Status);
|
||||
|
||||
// We can write the MXCSR state as is as there are no sensitive bits.
|
||||
internal::writeMXCSR(fpstate->MXCSR);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -32,7 +32,9 @@ add_libc_unittest(
|
|||
getenv_and_setenv_test.cpp
|
||||
DEPENDS
|
||||
libc.src.fenv.fegetenv
|
||||
libc.src.fenv.fegetround
|
||||
libc.src.fenv.fesetenv
|
||||
libc.src.fenv.fesetround
|
||||
libc.src.__support.FPUtil.fputil
|
||||
)
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "src/fenv/fegetenv.h"
|
||||
#include "src/fenv/fegetround.h"
|
||||
#include "src/fenv/fesetenv.h"
|
||||
#include "src/fenv/fesetround.h"
|
||||
|
||||
#include "src/__support/FPUtil/FEnvUtils.h"
|
||||
#include "utils/UnitTest/Test.h"
|
||||
|
@ -37,3 +39,34 @@ TEST(LlvmLibcFenvTest, GetEnvAndSetEnv) {
|
|||
ASSERT_EQ(__llvm_libc::fputil::testExcept(FE_ALL_EXCEPT) & e, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(LlvmLibcFenvTest, Set_FE_DFL_ENV) {
|
||||
// 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(FE_DFL_ENV), 0);
|
||||
// Setting the default env should clear all exceptions.
|
||||
ASSERT_EQ(__llvm_libc::fputil::testExcept(FE_ALL_EXCEPT) & e, 0);
|
||||
}
|
||||
|
||||
ASSERT_EQ(__llvm_libc::fesetround(FE_DOWNWARD), 0);
|
||||
ASSERT_EQ(__llvm_libc::fesetenv(FE_DFL_ENV), 0);
|
||||
// Setting the default env should set rounding mode to FE_TONEAREST.
|
||||
int rm = __llvm_libc::fegetround();
|
||||
EXPECT_EQ(rm, FE_TONEAREST);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue