1 //===-- TestMatchers.cpp ----------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "TestHelpers.h"
10 
11 #include "FPBits.h"
12 
13 #include <fenv.h>
14 #include <memory>
15 #include <setjmp.h>
16 #include <signal.h>
17 #include <string>
18 
19 namespace __llvm_libc {
20 namespace fputil {
21 namespace testing {
22 
23 // Return the first N hex digits of an integer as a string in upper case.
24 template <typename T>
25 cpp::EnableIfType<cpp::IsIntegral<T>::Value, std::string>
26 uintToHex(T X, size_t Length = sizeof(T) * 2) {
27   std::string s(Length, '0');
28 
29   for (auto it = s.rbegin(), end = s.rend(); it != end; ++it, X >>= 4) {
30     unsigned char Mod = static_cast<unsigned char>(X) & 15;
31     *it = (Mod < 10 ? '0' + Mod : 'a' + Mod - 10);
32   }
33 
34   return s;
35 }
36 
37 template <typename ValType>
38 cpp::EnableIfType<cpp::IsFloatingPointType<ValType>::Value, void>
39 describeValue(const char *label, ValType value,
40               testutils::StreamWrapper &stream) {
41   stream << label;
42 
43   FPBits<ValType> bits(value);
44   if (bits.isNaN()) {
45     stream << "(NaN)";
46   } else if (bits.isInf()) {
47     if (bits.getSign())
48       stream << "(-Infinity)";
49     else
50       stream << "(+Infinity)";
51   } else {
52     constexpr int exponentWidthInHex =
53         (fputil::ExponentWidth<ValType>::value - 1) / 4 + 1;
54     constexpr int mantissaWidthInHex =
55         (fputil::MantissaWidth<ValType>::value - 1) / 4 + 1;
56 
57     stream << "Sign: " << (bits.getSign() ? '1' : '0') << ", "
58            << "Exponent: 0x"
59            << uintToHex<uint16_t>(bits.getUnbiasedExponent(),
60                                   exponentWidthInHex)
61            << ", "
62            << "Mantissa: 0x"
63            << uintToHex<typename fputil::FPBits<ValType>::UIntType>(
64                   bits.getMantissa(), mantissaWidthInHex);
65   }
66 
67   stream << '\n';
68 }
69 
70 template void describeValue<float>(const char *, float,
71                                    testutils::StreamWrapper &);
72 template void describeValue<double>(const char *, double,
73                                     testutils::StreamWrapper &);
74 template void describeValue<long double>(const char *, long double,
75                                          testutils::StreamWrapper &);
76 
77 #if defined(_WIN32)
78 #define sigjmp_buf jmp_buf
79 #define sigsetjmp(buf, save) setjmp(buf)
80 #define siglongjmp(buf, val) longjmp(buf, val)
81 #endif
82 
83 static thread_local sigjmp_buf jumpBuffer;
84 static thread_local bool caughtExcept;
85 
86 static void sigfpeHandler(int sig) {
87   caughtExcept = true;
88   siglongjmp(jumpBuffer, -1);
89 }
90 
91 FPExceptMatcher::FPExceptMatcher(FunctionCaller *func) {
92   auto oldSIGFPEHandler = signal(SIGFPE, &sigfpeHandler);
93   std::unique_ptr<FunctionCaller> funcUP(func);
94 
95   caughtExcept = false;
96   fenv_t oldEnv;
97   fegetenv(&oldEnv);
98   if (sigsetjmp(jumpBuffer, 1) == 0)
99     funcUP->call();
100   // We restore the previous floating point environment after
101   // the call to the function which can potentially raise SIGFPE.
102   fesetenv(&oldEnv);
103   signal(SIGFPE, oldSIGFPEHandler);
104   exceptionRaised = caughtExcept;
105 }
106 
107 } // namespace testing
108 } // namespace fputil
109 } // namespace __llvm_libc
110