[libc] Add simple x86_64 floating point exception and rounding mode support.

Reviewed By: lntue

Differential Revision: https://reviews.llvm.org/D92546
This commit is contained in:
Siva Chandra Reddy
2020-12-01 11:39:48 -08:00
parent d93b8acd09
commit 4fff2a7e89
25 changed files with 745 additions and 1 deletions

View File

@@ -179,6 +179,22 @@ def MathAPI : PublicAPI<"math.h"> {
];
}
def FenvAPI: PublicAPI<"fenv.h"> {
let Macros = [
SimpleMacroDef<"FE_DIVBYZERO", "1">,
SimpleMacroDef<"FE_INEXACT", "2">,
SimpleMacroDef<"FE_INVALID", "4">,
SimpleMacroDef<"FE_OVERFLOW", "8">,
SimpleMacroDef<"FE_UNDERFLOW", "16">,
SimpleMacroDef<"FE_ALL_EXCEPT", "(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW)">,
SimpleMacroDef<"FE_DOWNWARD", "1">,
SimpleMacroDef<"FE_TONEAREST", "2">,
SimpleMacroDef<"FE_TOWARDZERO", "4">,
SimpleMacroDef<"FE_UPWARD", "8">,
];
}
def StringAPI : PublicAPI<"string.h"> {
let TypeDeclarations = [
SizeT,

View File

@@ -21,6 +21,13 @@ set(TARGET_LIBC_ENTRYPOINTS
# errno.h entrypoints
libc.src.errno.__errno_location
# fenv.h entrypoints
libc.src.fenv.feclearexcept
libc.src.fenv.fegetround
libc.src.fenv.fesetround
libc.src.fenv.feraiseexcept
libc.src.fenv.fetestexcept
# signal.h entrypoints
libc.src.signal.raise
libc.src.signal.sigaction

View File

@@ -25,6 +25,14 @@ add_gen_header(
.llvm_libc_common_h
)
add_gen_header(
fenv
DEF_FILE fenv.h.def
GEN_HDR fenv.h
DEPENDS
.llvm_libc_common_h
)
add_gen_header(
math
DEF_FILE math.h.def

16
libc/include/fenv.h.def Normal file
View File

@@ -0,0 +1,16 @@
//===-- C standard library header fenv.h ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_FENV_H
#define LLVM_LIBC_FENV_H
#include <__llvm-libc-common.h>
%%public_api()
#endif // LLVM_LIBC_FENV_H

View File

@@ -95,7 +95,53 @@ def StdC : StandardSpec<"stdc"> {
>,
]
>;
HeaderSpec Fenv = HeaderSpec<
"fenv.h",
[
Macro<"FE_DIVBYZERO">,
Macro<"FE_INEXACT">,
Macro<"FE_INVALID">,
Macro<"FE_OVERFLOW">,
Macro<"FE_UNDERFLOW">,
Macro<"FE_ALL_EXCEPT">,
Macro<"FE_DOWNWARD">,
Macro<"FE_TONEAREST">,
Macro<"FE_TOWARDZERO">,
Macro<"FE_UPWARD">
],
[], // Types
[], // Enumerations
[
FunctionSpec<
"feclearexcept",
RetValSpec<IntType>,
[ArgSpec<IntType>]
>,
FunctionSpec<
"fetestexcept",
RetValSpec<IntType>,
[ArgSpec<IntType>]
>,
FunctionSpec<
"feraiseexcept",
RetValSpec<IntType>,
[ArgSpec<IntType>]
>,
FunctionSpec<
"fesetround",
RetValSpec<IntType>,
[ArgSpec<IntType>]
>,
FunctionSpec<
"fegetround",
RetValSpec<IntType>,
[]
>,
]
>;
HeaderSpec String = HeaderSpec<
"string.h",
[
@@ -491,6 +537,7 @@ def StdC : StandardSpec<"stdc"> {
Assert,
CType,
Errno,
Fenv,
Math,
String,
StdIO,

View File

@@ -1,6 +1,7 @@
add_subdirectory(assert)
add_subdirectory(ctype)
add_subdirectory(errno)
add_subdirectory(fenv)
add_subdirectory(math)
add_subdirectory(signal)
add_subdirectory(stdio)

View File

@@ -0,0 +1,64 @@
add_entrypoint_object(
fegetround
SRCS
fegetround.cpp
HDRS
fegetround.h
DEPENDS
libc.include.fenv
libc.utils.FPUtil.fputil
COMPILE_OPTIONS
-O2
)
add_entrypoint_object(
fesetround
SRCS
fesetround.cpp
HDRS
fesetround.h
DEPENDS
libc.include.fenv
libc.utils.FPUtil.fputil
COMPILE_OPTIONS
-O2
)
add_entrypoint_object(
feclearexcept
SRCS
feclearexcept.cpp
HDRS
feclearexcept.h
DEPENDS
libc.include.fenv
libc.utils.FPUtil.fputil
COMPILE_OPTIONS
-O2
)
add_entrypoint_object(
feraiseexcept
SRCS
feraiseexcept.cpp
HDRS
feraiseexcept.h
DEPENDS
libc.include.fenv
libc.utils.FPUtil.fputil
COMPILE_OPTIONS
-O2
)
add_entrypoint_object(
fetestexcept
SRCS
fetestexcept.cpp
HDRS
fetestexcept.h
DEPENDS
libc.include.fenv
libc.utils.FPUtil.fputil
COMPILE_OPTIONS
-O2
)

View File

@@ -0,0 +1,18 @@
//===-- Implementation of feclearexcept function --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/__support/common.h"
#include "utils/FPUtil/FEnv.h"
namespace __llvm_libc {
int LLVM_LIBC_ENTRYPOINT(feclearexcept)(int e) {
return fputil::clearExcept(e);
}
} // namespace __llvm_libc

View File

@@ -0,0 +1,18 @@
//===-- Implementation header for feclearexcept -----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_FENV_FECLEAREXCEPT_H
#define LLVM_LIBC_SRC_FENV_FECLEAREXCEPT_H
namespace __llvm_libc {
int feclearexcept(int);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_FENV_FECLEAREXCEPT_H

View File

@@ -0,0 +1,16 @@
//===-- Implementation of fegetround function -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/__support/common.h"
#include "utils/FPUtil/FEnv.h"
namespace __llvm_libc {
int LLVM_LIBC_ENTRYPOINT(fegetround)() { return fputil::getRound(); }
} // namespace __llvm_libc

View File

@@ -0,0 +1,18 @@
//===-- Implementation header for fegetround --------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_FENV_FEGETROUND_H
#define LLVM_LIBC_SRC_FENV_FEGETROUND_H
namespace __llvm_libc {
int fegetround();
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_FENV_FEGETROUND_H

View File

@@ -0,0 +1,18 @@
//===-- Implementation of feraiseexcept function --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/__support/common.h"
#include "utils/FPUtil/FEnv.h"
namespace __llvm_libc {
int LLVM_LIBC_ENTRYPOINT(feraiseexcept)(int e) {
return fputil::raiseExcept(e);
}
} // namespace __llvm_libc

View File

@@ -0,0 +1,18 @@
//===-- Implementation header for feraiseexcept -----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_FENV_FERAISEEXCEPT_H
#define LLVM_LIBC_SRC_FENV_FERAISEEXCEPT_H
namespace __llvm_libc {
int feraiseexcept(int);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_FENV_FERAISEEXCEPT_H

View File

@@ -0,0 +1,16 @@
//===-- Implementation of fesetround function -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/__support/common.h"
#include "utils/FPUtil/FEnv.h"
namespace __llvm_libc {
int LLVM_LIBC_ENTRYPOINT(fesetround)(int m) { return fputil::setRound(m); }
} // namespace __llvm_libc

View File

@@ -0,0 +1,18 @@
//===-- Implementation header for fesetround --------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_FENV_FESETROUND_H
#define LLVM_LIBC_SRC_FENV_FESETROUND_H
namespace __llvm_libc {
int fesetround(int);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_FENV_FESETROUND_H

View File

@@ -0,0 +1,16 @@
//===-- Implementation of fetestexcept function ---------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/__support/common.h"
#include "utils/FPUtil/FEnv.h"
namespace __llvm_libc {
int LLVM_LIBC_ENTRYPOINT(fetestexcept)(int e) { return fputil::testExcept(e); }
} // namespace __llvm_libc

View File

@@ -0,0 +1,18 @@
//===-- Implementation header for fetestexpect ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_FENV_FETESTEXCEPT_H
#define LLVM_LIBC_SRC_FENV_FETESTEXCEPT_H
namespace __llvm_libc {
int fetestexcept(int);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_FENV_FETESTEXCEPT_H

View File

@@ -1,6 +1,7 @@
add_subdirectory(assert)
add_subdirectory(ctype)
add_subdirectory(errno)
add_subdirectory(fenv)
add_subdirectory(math)
add_subdirectory(signal)
add_subdirectory(stdio)

View File

@@ -0,0 +1,24 @@
add_libc_testsuite(libc_fenv_unittests)
add_libc_unittest(
rounding_mode_test
SUITE
libc_fenv_unittests
SRCS
rounding_mode_test.cpp
DEPENDS
libc.src.fenv.fegetround
libc.src.fenv.fesetround
)
add_libc_unittest(
exception_status_test
SUITE
libc_fenv_unittests
SRCS
exception_status_test.cpp
DEPENDS
libc.src.fenv.feclearexcept
libc.src.fenv.feraiseexcept
libc.src.fenv.fetestexcept
)

View File

@@ -0,0 +1,108 @@
//===-- Unittests for feclearexcept, feraiseexcept and fetestexpect -------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/fenv/feclearexcept.h"
#include "src/fenv/feraiseexcept.h"
#include "src/fenv/fetestexcept.h"
#include "utils/UnitTest/Test.h"
#include <fenv.h>
TEST(ExceptionStatusTest, RaiseAndTest) {
int excepts[] = {FE_DIVBYZERO, FE_INVALID, FE_INEXACT, FE_OVERFLOW,
FE_UNDERFLOW};
for (int e : excepts) {
int r = __llvm_libc::feraiseexcept(e);
EXPECT_EQ(r, 0);
int s = __llvm_libc::fetestexcept(e);
EXPECT_EQ(s, e);
r = __llvm_libc::feclearexcept(e);
EXPECT_EQ(r, 0);
s = __llvm_libc::fetestexcept(e);
EXPECT_EQ(s, 0);
}
for (int e1 : excepts) {
for (int e2 : excepts) {
int e = e1 | e2;
int r = __llvm_libc::feraiseexcept(e);
EXPECT_EQ(r, 0);
int s = __llvm_libc::fetestexcept(e);
EXPECT_EQ(s, e);
r = __llvm_libc::feclearexcept(e);
EXPECT_EQ(r, 0);
s = __llvm_libc::fetestexcept(e);
EXPECT_EQ(s, 0);
}
}
for (int e1 : excepts) {
for (int e2 : excepts) {
for (int e3 : excepts) {
int e = e1 | e2 | e3;
int r = __llvm_libc::feraiseexcept(e);
EXPECT_EQ(r, 0);
int s = __llvm_libc::fetestexcept(e);
EXPECT_EQ(s, e);
r = __llvm_libc::feclearexcept(e);
EXPECT_EQ(r, 0);
s = __llvm_libc::fetestexcept(e);
EXPECT_EQ(s, 0);
}
}
}
for (int e1 : excepts) {
for (int e2 : excepts) {
for (int e3 : excepts) {
for (int e4 : excepts) {
int e = e1 | e2 | e3 | e4;
int r = __llvm_libc::feraiseexcept(e);
EXPECT_EQ(r, 0);
int s = __llvm_libc::fetestexcept(e);
EXPECT_EQ(s, e);
r = __llvm_libc::feclearexcept(e);
EXPECT_EQ(r, 0);
s = __llvm_libc::fetestexcept(e);
EXPECT_EQ(s, 0);
}
}
}
}
for (int e1 : excepts) {
for (int e2 : excepts) {
for (int e3 : excepts) {
for (int e4 : excepts) {
for (int e5 : excepts) {
int e = e1 | e2 | e3 | e4 | e5;
int r = __llvm_libc::feraiseexcept(e);
EXPECT_EQ(r, 0);
int s = __llvm_libc::fetestexcept(e);
EXPECT_EQ(s, e);
r = __llvm_libc::feclearexcept(e);
EXPECT_EQ(r, 0);
s = __llvm_libc::fetestexcept(e);
EXPECT_EQ(s, 0);
}
}
}
}
}
int r = __llvm_libc::feraiseexcept(FE_ALL_EXCEPT);
EXPECT_EQ(r, 0);
int s = __llvm_libc::fetestexcept(FE_ALL_EXCEPT);
EXPECT_EQ(s, FE_ALL_EXCEPT);
}

View File

@@ -0,0 +1,36 @@
//===-- Unittests for fegetround and fesetround ---------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/fenv/fegetround.h"
#include "src/fenv/fesetround.h"
#include "utils/UnitTest/Test.h"
#include <fenv.h>
TEST(RoundingModeTest, SetAndGet) {
int s = __llvm_libc::fesetround(FE_TONEAREST);
EXPECT_EQ(s, 0);
int rm = __llvm_libc::fegetround();
EXPECT_EQ(rm, FE_TONEAREST);
s = __llvm_libc::fesetround(FE_UPWARD);
EXPECT_EQ(s, 0);
rm = __llvm_libc::fegetround();
EXPECT_EQ(rm, FE_UPWARD);
s = __llvm_libc::fesetround(FE_DOWNWARD);
EXPECT_EQ(s, 0);
rm = __llvm_libc::fegetround();
EXPECT_EQ(rm, FE_DOWNWARD);
s = __llvm_libc::fesetround(FE_TOWARDZERO);
EXPECT_EQ(s, 0);
rm = __llvm_libc::fegetround();
EXPECT_EQ(rm, FE_TOWARDZERO);
}

View File

@@ -4,14 +4,22 @@ else()
set(LONG_DOUBLE_HDR)
endif()
if(EXISTS ${LIBC_TARGET_MACHINE})
set(FENV_IMPL ${LIBC_TARGET_MACHINE}/FEnv.h)
else()
set(FENV_IMPL DummyFEnv.h)
endif()
add_header_library(
fputil
HDRS
${LONG_DOUBLE_HDR}
${FENV_IMPL}
BasicOperations.h
BitPatterns.h
ClassificationFunctions.h
DivisionAndRemainderOperations.h
FEnv.h
FloatOperations.h
FloatProperties.h
FPBits.h

View File

@@ -0,0 +1,32 @@
//===-- Dummy floating point environment manipulation functins --*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_UTILS_FPUTIL_DUMMY_FENV_H
#define LLVM_LIBC_UTILS_FPUTIL_DUMMY_FENV_H
#include <math.h>
namespace __llvm_libc {
namespace fputil {
// All dummy functions silently succeed.
int clearExcept(int) { return 0; }
int testExcept(int) { return 0; }
int raiseExcept(int) { return 0; }
int getRound() { return FE_TONEAREST; }
int setRound(int) { return 0; }
} // namespace fputil
} // namespace __llvm_libc
#endif // LLVM_LIBC_UTILS_FPUTIL_DUMMY_FENV_H

18
libc/utils/FPUtil/FEnv.h Normal file
View File

@@ -0,0 +1,18 @@
//===-- Utilities for manipulating floating point environment ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_UTILS_FPUTIL_FENV_H
#define LLVM_LIBC_UTILS_FPUTIL_FENV_H
#ifdef __x86_64__
#include "x86_64/FEnv.h"
#else
#include "DummyFEnv.h"
#endif
#endif // LLVM_LIBC_UTILS_FPUTIL_FP_BITS_H

View File

@@ -0,0 +1,184 @@
//===-- x86_64 floating point env manipulation functions --------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_UTILS_FPUTIL_X86_64_FENV_H
#define LLVM_LIBC_UTILS_FPUTIL_X86_64_FENV_H
#include <fenv.h>
#include <stdint.h>
#include <xmmintrin.h>
namespace __llvm_libc {
namespace fputil {
namespace internal {
// Normally, one should be able to define FE_* macros to the exact rounding mode
// encodings. However, since we want LLVM libc to be compiled against headers
// from other libcs, we cannot assume that FE_* macros are always defined in
// such a manner. So, we will define enums corresponding to the x86_64 bit
// encodings. The implementations can map from FE_* to the corresponding enum
// values.
// The rounding control values in the x87 control register and the MXCSR
// register have the same 2-bit enoding but have different bit positions.
// See below for the bit positions.
struct RoundingControlValue {
static constexpr uint16_t ToNearest = 0x0;
static constexpr uint16_t Downward = 0x1;
static constexpr uint16_t Upward = 0x2;
static constexpr uint16_t TowardZero = 0x3;
};
static constexpr uint16_t X87RoundingControlBitPosition = 10;
static constexpr uint16_t MXCSRRoundingControlBitPosition = 13;
// The exception flags in the x87 status register and the MXCSR have the same
// encoding as well as the same bit positions.
struct ExceptionFlags {
static constexpr uint16_t Invalid = 0x1;
static constexpr uint16_t Denormal = 0x2; // This flag is not used
static constexpr uint16_t DivByZero = 0x4;
static constexpr uint16_t Overflow = 0x8;
static constexpr uint16_t Underflow = 0x10;
static constexpr uint16_t Inexact = 0x20;
};
// Exception flags are individual bits in the corresponding registers.
// So, we just OR the bit values to get the full set of exceptions.
static inline uint16_t getStatusValueForExcept(int excepts) {
// We will make use of the fact that exception control bits are single
// bit flags in the control registers.
return (excepts & FE_INVALID ? ExceptionFlags::Invalid : 0) |
(excepts & FE_DIVBYZERO ? ExceptionFlags::DivByZero : 0) |
(excepts & FE_OVERFLOW ? ExceptionFlags::Overflow : 0) |
(excepts & FE_UNDERFLOW ? ExceptionFlags::Underflow : 0) |
(excepts & FE_INEXACT ? ExceptionFlags::Inexact : 0);
}
static inline int exceptionStatusToMacro(uint16_t status) {
return (status & ExceptionFlags::Invalid ? FE_INVALID : 0) |
(status & ExceptionFlags::DivByZero ? FE_DIVBYZERO : 0) |
(status & ExceptionFlags::Overflow ? FE_OVERFLOW : 0) |
(status & ExceptionFlags::Underflow ? FE_UNDERFLOW : 0) |
(status & ExceptionFlags::Inexact ? FE_INEXACT : 0);
}
static inline uint16_t getX87ControlWord() {
uint16_t w;
__asm__ __volatile__("fnstcw %0" : "=m"(w)::);
return w;
}
static inline void writeX87ControlWord(uint16_t w) {
__asm__ __volatile__("fldcw %0" : : "m"(w) :);
}
static inline uint16_t getX87StatusWord() {
uint16_t w;
__asm__ __volatile__("fnstsw %0" : "=m"(w)::);
return w;
}
static inline void clearX87Exceptions() {
__asm__ __volatile__("fnclex" : : :);
}
} // namespace internal
static inline int clearExcept(int excepts) {
// An instruction to write to x87 status word ins't available. So, we
// just clear all of the x87 exceptions.
// TODO: One can potentially use fegetenv/fesetenv to clear only the
// listed exceptions in the x87 status word. We can do this if it is
// really required.
internal::clearX87Exceptions();
uint32_t mxcsr = _mm_getcsr();
mxcsr &= ~internal::getStatusValueForExcept(excepts);
_mm_setcsr(mxcsr);
return 0;
}
static inline int testExcept(int excepts) {
uint16_t statusValue = internal::getStatusValueForExcept(excepts);
// Check both x87 status word and MXCSR.
return internal::exceptionStatusToMacro(
(statusValue & internal::getX87StatusWord()) |
(statusValue & _mm_getcsr()));
}
static inline int raiseExcept(int excepts) {
// It is enough to set the exception flags in MXCSR.
// TODO: Investigate if each exception has to be raised one at a time
// followed with an fwait instruction before writing the flag for the
// next exception.
uint16_t statusValue = internal::getStatusValueForExcept(excepts);
uint32_t sse = _mm_getcsr();
sse = sse | statusValue;
_mm_setcsr(sse);
return 0;
}
static inline int getRound() {
uint16_t bitValue =
(_mm_getcsr() >> internal::MXCSRRoundingControlBitPosition) & 0x3;
switch (bitValue) {
case internal::RoundingControlValue::ToNearest:
return FE_TONEAREST;
case internal::RoundingControlValue::Downward:
return FE_DOWNWARD;
case internal::RoundingControlValue::Upward:
return FE_UPWARD;
case internal::RoundingControlValue::TowardZero:
return FE_TOWARDZERO;
default:
return -1; // Error value.
}
}
static inline int setRound(int mode) {
uint16_t bitValue;
switch (mode) {
case FE_TONEAREST:
bitValue = internal::RoundingControlValue::ToNearest;
break;
case FE_DOWNWARD:
bitValue = internal::RoundingControlValue::Downward;
break;
case FE_UPWARD:
bitValue = internal::RoundingControlValue::Upward;
break;
case FE_TOWARDZERO:
bitValue = internal::RoundingControlValue::TowardZero;
break;
default:
return 1; // To indicate failure
}
uint16_t x87Value = bitValue << internal::X87RoundingControlBitPosition;
uint16_t x87Control = internal::getX87ControlWord();
x87Control =
(x87Control & ~(0x3 << internal::X87RoundingControlBitPosition)) |
x87Value;
internal::writeX87ControlWord(x87Control);
uint32_t mxcsrValue = bitValue << internal::MXCSRRoundingControlBitPosition;
uint32_t mxcsrControl = _mm_getcsr();
mxcsrControl =
(mxcsrControl & ~(0x3 << internal::MXCSRRoundingControlBitPosition)) |
mxcsrValue;
_mm_setcsr(mxcsrControl);
return 0;
}
} // namespace fputil
} // namespace __llvm_libc
#endif // LLVM_LIBC_UTILS_FPUTIL_X86_64_FENV_H