1 //===-- MPFRUtils.h ---------------------------------------------*- 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 #ifndef LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H 10 #define LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H 11 12 #include "utils/CPP/TypeTraits.h" 13 #include "utils/UnitTest/Test.h" 14 15 #include <stdint.h> 16 17 namespace __llvm_libc { 18 namespace testing { 19 namespace mpfr { 20 21 enum class Operation : int { 22 // Operations with take a single floating point number as input 23 // and produce a single floating point number as output. The input 24 // and output floating point numbers are of the same kind. 25 BeginUnaryOperationsSingleOutput, 26 Abs, 27 Ceil, 28 Cos, 29 Exp, 30 Exp2, 31 Expm1, 32 Floor, 33 Round, 34 Sin, 35 Sqrt, 36 Tan, 37 Trunc, 38 EndUnaryOperationsSingleOutput, 39 40 // Operations which take a single floating point nubmer as input 41 // but produce two outputs. The first ouput is a floating point 42 // number of the same type as the input. The second output is of type 43 // 'int'. 44 BeginUnaryOperationsTwoOutputs, 45 Frexp, // Floating point output, the first output, is the fractional part. 46 EndUnaryOperationsTwoOutputs, 47 48 // Operations wich take two floating point nubmers of the same type as 49 // input and produce a single floating point number of the same type as 50 // output. 51 BeginBinaryOperationsSingleOutput, 52 Hypot, 53 EndBinaryOperationsSingleOutput, 54 55 // Operations which take two floating point numbers of the same type as 56 // input and produce two outputs. The first output is a floating nubmer of 57 // the same type as the inputs. The second output is af type 'int'. 58 BeginBinaryOperationsTwoOutputs, 59 RemQuo, // The first output, the floating point output, is the remainder. 60 EndBinaryOperationsTwoOutputs, 61 62 // Operations which take three floating point nubmers of the same type as 63 // input and produce a single floating point number of the same type as 64 // output. 65 BeginTernaryOperationsSingleOuput, 66 Fma, 67 EndTernaryOperationsSingleOutput, 68 }; 69 70 template <typename T> struct BinaryInput { 71 static_assert( 72 __llvm_libc::cpp::IsFloatingPointType<T>::Value, 73 "Template parameter of BinaryInput must be a floating point type."); 74 75 using Type = T; 76 T x, y; 77 }; 78 79 template <typename T> struct TernaryInput { 80 static_assert( 81 __llvm_libc::cpp::IsFloatingPointType<T>::Value, 82 "Template parameter of TernaryInput must be a floating point type."); 83 84 using Type = T; 85 T x, y, z; 86 }; 87 88 template <typename T> struct BinaryOutput { 89 T f; 90 int i; 91 }; 92 93 namespace internal { 94 95 template <typename T1, typename T2> 96 struct AreMatchingBinaryInputAndBinaryOutput { 97 static constexpr bool value = false; 98 }; 99 100 template <typename T> 101 struct AreMatchingBinaryInputAndBinaryOutput<BinaryInput<T>, BinaryOutput<T>> { 102 static constexpr bool value = cpp::IsFloatingPointType<T>::Value; 103 }; 104 105 template <typename T> 106 bool compareUnaryOperationSingleOutput(Operation op, T input, T libcOutput, 107 double t); 108 template <typename T> 109 bool compareUnaryOperationTwoOutputs(Operation op, T input, 110 const BinaryOutput<T> &libcOutput, 111 double t); 112 template <typename T> 113 bool compareBinaryOperationTwoOutputs(Operation op, const BinaryInput<T> &input, 114 const BinaryOutput<T> &libcOutput, 115 double t); 116 117 template <typename T> 118 bool compareBinaryOperationOneOutput(Operation op, const BinaryInput<T> &input, 119 T libcOutput, double t); 120 121 template <typename T> 122 bool compareTernaryOperationOneOutput(Operation op, 123 const TernaryInput<T> &input, 124 T libcOutput, double t); 125 126 template <typename T> 127 void explainUnaryOperationSingleOutputError(Operation op, T input, T matchValue, 128 testutils::StreamWrapper &OS); 129 template <typename T> 130 void explainUnaryOperationTwoOutputsError(Operation op, T input, 131 const BinaryOutput<T> &matchValue, 132 testutils::StreamWrapper &OS); 133 template <typename T> 134 void explainBinaryOperationTwoOutputsError(Operation op, 135 const BinaryInput<T> &input, 136 const BinaryOutput<T> &matchValue, 137 testutils::StreamWrapper &OS); 138 139 template <typename T> 140 void explainBinaryOperationOneOutputError(Operation op, 141 const BinaryInput<T> &input, 142 T matchValue, 143 testutils::StreamWrapper &OS); 144 145 template <typename T> 146 void explainTernaryOperationOneOutputError(Operation op, 147 const TernaryInput<T> &input, 148 T matchValue, 149 testutils::StreamWrapper &OS); 150 151 template <Operation op, typename InputType, typename OutputType> 152 class MPFRMatcher : public testing::Matcher<OutputType> { 153 InputType input; 154 OutputType matchValue; 155 double ulpTolerance; 156 157 public: 158 MPFRMatcher(InputType testInput, double ulpTolerance) 159 : input(testInput), ulpTolerance(ulpTolerance) {} 160 161 bool match(OutputType libcResult) { 162 matchValue = libcResult; 163 return match(input, matchValue, ulpTolerance); 164 } 165 166 void explainError(testutils::StreamWrapper &OS) override { 167 explainError(input, matchValue, OS); 168 } 169 170 private: 171 template <typename T> static bool match(T in, T out, double tolerance) { 172 return compareUnaryOperationSingleOutput(op, in, out, tolerance); 173 } 174 175 template <typename T> 176 static bool match(T in, const BinaryOutput<T> &out, double tolerance) { 177 return compareUnaryOperationTwoOutputs(op, in, out, tolerance); 178 } 179 180 template <typename T> 181 static bool match(const BinaryInput<T> &in, T out, double tolerance) { 182 return compareBinaryOperationOneOutput(op, in, out, tolerance); 183 } 184 185 template <typename T> 186 static bool match(BinaryInput<T> in, const BinaryOutput<T> &out, 187 double tolerance) { 188 return compareBinaryOperationTwoOutputs(op, in, out, tolerance); 189 } 190 191 template <typename T> 192 static bool match(const TernaryInput<T> &in, T out, double tolerance) { 193 return compareTernaryOperationOneOutput(op, in, out, tolerance); 194 } 195 196 template <typename T> 197 static void explainError(T in, T out, testutils::StreamWrapper &OS) { 198 explainUnaryOperationSingleOutputError(op, in, out, OS); 199 } 200 201 template <typename T> 202 static void explainError(T in, const BinaryOutput<T> &out, 203 testutils::StreamWrapper &OS) { 204 explainUnaryOperationTwoOutputsError(op, in, out, OS); 205 } 206 207 template <typename T> 208 static void explainError(const BinaryInput<T> &in, const BinaryOutput<T> &out, 209 testutils::StreamWrapper &OS) { 210 explainBinaryOperationTwoOutputsError(op, in, out, OS); 211 } 212 213 template <typename T> 214 static void explainError(const BinaryInput<T> &in, T out, 215 testutils::StreamWrapper &OS) { 216 explainBinaryOperationOneOutputError(op, in, out, OS); 217 } 218 219 template <typename T> 220 static void explainError(const TernaryInput<T> &in, T out, 221 testutils::StreamWrapper &OS) { 222 explainTernaryOperationOneOutputError(op, in, out, OS); 223 } 224 }; 225 226 } // namespace internal 227 228 // Return true if the input and ouput types for the operation op are valid 229 // types. 230 template <Operation op, typename InputType, typename OutputType> 231 constexpr bool isValidOperation() { 232 return (Operation::BeginUnaryOperationsSingleOutput < op && 233 op < Operation::EndUnaryOperationsSingleOutput && 234 cpp::IsSame<InputType, OutputType>::Value && 235 cpp::IsFloatingPointType<InputType>::Value) || 236 (Operation::BeginUnaryOperationsTwoOutputs < op && 237 op < Operation::EndUnaryOperationsTwoOutputs && 238 cpp::IsFloatingPointType<InputType>::Value && 239 cpp::IsSame<OutputType, BinaryOutput<InputType>>::Value) || 240 (Operation::BeginBinaryOperationsSingleOutput < op && 241 op < Operation::EndBinaryOperationsSingleOutput && 242 cpp::IsFloatingPointType<OutputType>::Value && 243 cpp::IsSame<InputType, BinaryInput<OutputType>>::Value) || 244 (Operation::BeginBinaryOperationsTwoOutputs < op && 245 op < Operation::EndBinaryOperationsTwoOutputs && 246 internal::AreMatchingBinaryInputAndBinaryOutput<InputType, 247 OutputType>::value) || 248 (Operation::BeginTernaryOperationsSingleOuput < op && 249 op < Operation::EndTernaryOperationsSingleOutput && 250 cpp::IsFloatingPointType<OutputType>::Value && 251 cpp::IsSame<InputType, TernaryInput<OutputType>>::Value); 252 } 253 254 template <Operation op, typename InputType, typename OutputType> 255 __attribute__((no_sanitize("address"))) 256 cpp::EnableIfType<isValidOperation<op, InputType, OutputType>(), 257 internal::MPFRMatcher<op, InputType, OutputType>> 258 getMPFRMatcher(InputType input, OutputType outputUnused, double t) { 259 return internal::MPFRMatcher<op, InputType, OutputType>(input, t); 260 } 261 262 enum class RoundingMode : uint8_t { Upward, Downward, TowardZero, Nearest }; 263 264 template <typename T> T Round(T x, RoundingMode mode); 265 266 template <typename T> bool RoundToLong(T x, long &result); 267 template <typename T> bool RoundToLong(T x, RoundingMode mode, long &result); 268 269 } // namespace mpfr 270 } // namespace testing 271 } // namespace __llvm_libc 272 273 #define EXPECT_MPFR_MATCH(op, input, matchValue, tolerance) \ 274 EXPECT_THAT(matchValue, __llvm_libc::testing::mpfr::getMPFRMatcher<op>( \ 275 input, matchValue, tolerance)) 276 277 #define ASSERT_MPFR_MATCH(op, input, matchValue, tolerance) \ 278 ASSERT_THAT(matchValue, __llvm_libc::testing::mpfr::getMPFRMatcher<op>( \ 279 input, matchValue, tolerance)) 280 281 #endif // LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H 282