| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |