[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:
Siva Chandra Reddy 2021-02-15 22:36:55 -08:00
parent 53e83afcaf
commit b7e05c874b
25 changed files with 707 additions and 19 deletions

View File

@ -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

View File

@ -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"> {

View File

@ -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

View File

@ -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>]
>,
]
>;

View File

@ -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
)

View File

@ -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

20
libc/src/fenv/fegetenv.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

20
libc/src/fenv/fesetenv.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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