Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / libc / utils / MPFRWrapper / MPFRUtils.h
blobb9573e89f25a4a9226ffe29370deba56941d98fa
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 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
10 #define LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
12 #include "src/__support/CPP/type_traits.h"
13 #include "test/UnitTest/RoundingModeUtils.h"
14 #include "test/UnitTest/Test.h"
16 #include <stdint.h>
18 namespace LIBC_NAMESPACE {
19 namespace testing {
20 namespace mpfr {
22 enum class Operation : int {
23 // Operations with take a single floating point number as input
24 // and produce a single floating point number as output. The input
25 // and output floating point numbers are of the same kind.
26 BeginUnaryOperationsSingleOutput,
27 Abs,
28 Acos,
29 Acosh,
30 Asin,
31 Asinh,
32 Atan,
33 Atanh,
34 Ceil,
35 Cos,
36 Cosh,
37 Erf,
38 Exp,
39 Exp2,
40 Exp10,
41 Expm1,
42 Floor,
43 Log,
44 Log2,
45 Log10,
46 Log1p,
47 Mod2PI,
48 ModPIOver2,
49 ModPIOver4,
50 Round,
51 Sin,
52 Sinh,
53 Sqrt,
54 Tan,
55 Tanh,
56 Trunc,
57 EndUnaryOperationsSingleOutput,
59 // Operations which take a single floating point nubmer as input
60 // but produce two outputs. The first ouput is a floating point
61 // number of the same type as the input. The second output is of type
62 // 'int'.
63 BeginUnaryOperationsTwoOutputs,
64 Frexp, // Floating point output, the first output, is the fractional part.
65 EndUnaryOperationsTwoOutputs,
67 // Operations wich take two floating point nubmers of the same type as
68 // input and produce a single floating point number of the same type as
69 // output.
70 BeginBinaryOperationsSingleOutput,
71 Fmod,
72 Hypot,
73 EndBinaryOperationsSingleOutput,
75 // Operations which take two floating point numbers of the same type as
76 // input and produce two outputs. The first output is a floating nubmer of
77 // the same type as the inputs. The second output is af type 'int'.
78 BeginBinaryOperationsTwoOutputs,
79 RemQuo, // The first output, the floating point output, is the remainder.
80 EndBinaryOperationsTwoOutputs,
82 // Operations which take three floating point nubmers of the same type as
83 // input and produce a single floating point number of the same type as
84 // output.
85 BeginTernaryOperationsSingleOuput,
86 Fma,
87 EndTernaryOperationsSingleOutput,
90 using LIBC_NAMESPACE::fputil::testing::ForceRoundingMode;
91 using LIBC_NAMESPACE::fputil::testing::RoundingMode;
93 template <typename T> struct BinaryInput {
94 static_assert(
95 LIBC_NAMESPACE::cpp::is_floating_point_v<T>,
96 "Template parameter of BinaryInput must be a floating point type.");
98 using Type = T;
99 T x, y;
102 template <typename T> struct TernaryInput {
103 static_assert(
104 LIBC_NAMESPACE::cpp::is_floating_point_v<T>,
105 "Template parameter of TernaryInput must be a floating point type.");
107 using Type = T;
108 T x, y, z;
111 template <typename T> struct BinaryOutput {
112 T f;
113 int i;
116 namespace internal {
118 template <typename T1, typename T2>
119 struct AreMatchingBinaryInputAndBinaryOutput {
120 static constexpr bool VALUE = false;
123 template <typename T>
124 struct AreMatchingBinaryInputAndBinaryOutput<BinaryInput<T>, BinaryOutput<T>> {
125 static constexpr bool VALUE = cpp::is_floating_point_v<T>;
128 template <typename T>
129 bool compare_unary_operation_single_output(Operation op, T input, T libc_output,
130 double ulp_tolerance,
131 RoundingMode rounding);
132 template <typename T>
133 bool compare_unary_operation_two_outputs(Operation op, T input,
134 const BinaryOutput<T> &libc_output,
135 double ulp_tolerance,
136 RoundingMode rounding);
137 template <typename T>
138 bool compare_binary_operation_two_outputs(Operation op,
139 const BinaryInput<T> &input,
140 const BinaryOutput<T> &libc_output,
141 double ulp_tolerance,
142 RoundingMode rounding);
144 template <typename T>
145 bool compare_binary_operation_one_output(Operation op,
146 const BinaryInput<T> &input,
147 T libc_output, double ulp_tolerance,
148 RoundingMode rounding);
150 template <typename T>
151 bool compare_ternary_operation_one_output(Operation op,
152 const TernaryInput<T> &input,
153 T libc_output, double ulp_tolerance,
154 RoundingMode rounding);
156 template <typename T>
157 void explain_unary_operation_single_output_error(Operation op, T input,
158 T match_value,
159 double ulp_tolerance,
160 RoundingMode rounding);
161 template <typename T>
162 void explain_unary_operation_two_outputs_error(
163 Operation op, T input, const BinaryOutput<T> &match_value,
164 double ulp_tolerance, RoundingMode rounding);
165 template <typename T>
166 void explain_binary_operation_two_outputs_error(
167 Operation op, const BinaryInput<T> &input,
168 const BinaryOutput<T> &match_value, double ulp_tolerance,
169 RoundingMode rounding);
171 template <typename T>
172 void explain_binary_operation_one_output_error(Operation op,
173 const BinaryInput<T> &input,
174 T match_value,
175 double ulp_tolerance,
176 RoundingMode rounding);
178 template <typename T>
179 void explain_ternary_operation_one_output_error(Operation op,
180 const TernaryInput<T> &input,
181 T match_value,
182 double ulp_tolerance,
183 RoundingMode rounding);
185 template <Operation op, bool silent, typename InputType, typename OutputType>
186 class MPFRMatcher : public testing::Matcher<OutputType> {
187 InputType input;
188 OutputType match_value;
189 double ulp_tolerance;
190 RoundingMode rounding;
192 public:
193 MPFRMatcher(InputType testInput, double ulp_tolerance, RoundingMode rounding)
194 : input(testInput), ulp_tolerance(ulp_tolerance), rounding(rounding) {}
196 bool match(OutputType libcResult) {
197 match_value = libcResult;
198 return match(input, match_value);
201 // This method is marked with NOLINT because the name `explainError` does not
202 // conform to the coding style.
203 void explainError() override { // NOLINT
204 explain_error(input, match_value);
207 // Whether the `explainError` step is skipped or not.
208 bool is_silent() const override { return silent; }
210 private:
211 template <typename T> bool match(T in, T out) {
212 return compare_unary_operation_single_output(op, in, out, ulp_tolerance,
213 rounding);
216 template <typename T> bool match(T in, const BinaryOutput<T> &out) {
217 return compare_unary_operation_two_outputs(op, in, out, ulp_tolerance,
218 rounding);
221 template <typename T> bool match(const BinaryInput<T> &in, T out) {
222 return compare_binary_operation_one_output(op, in, out, ulp_tolerance,
223 rounding);
226 template <typename T>
227 bool match(BinaryInput<T> in, const BinaryOutput<T> &out) {
228 return compare_binary_operation_two_outputs(op, in, out, ulp_tolerance,
229 rounding);
232 template <typename T> bool match(const TernaryInput<T> &in, T out) {
233 return compare_ternary_operation_one_output(op, in, out, ulp_tolerance,
234 rounding);
237 template <typename T> void explain_error(T in, T out) {
238 explain_unary_operation_single_output_error(op, in, out, ulp_tolerance,
239 rounding);
242 template <typename T> void explain_error(T in, const BinaryOutput<T> &out) {
243 explain_unary_operation_two_outputs_error(op, in, out, ulp_tolerance,
244 rounding);
247 template <typename T>
248 void explain_error(const BinaryInput<T> &in, const BinaryOutput<T> &out) {
249 explain_binary_operation_two_outputs_error(op, in, out, ulp_tolerance,
250 rounding);
253 template <typename T> void explain_error(const BinaryInput<T> &in, T out) {
254 explain_binary_operation_one_output_error(op, in, out, ulp_tolerance,
255 rounding);
258 template <typename T> void explain_error(const TernaryInput<T> &in, T out) {
259 explain_ternary_operation_one_output_error(op, in, out, ulp_tolerance,
260 rounding);
264 } // namespace internal
266 // Return true if the input and ouput types for the operation op are valid
267 // types.
268 template <Operation op, typename InputType, typename OutputType>
269 constexpr bool is_valid_operation() {
270 return (Operation::BeginUnaryOperationsSingleOutput < op &&
271 op < Operation::EndUnaryOperationsSingleOutput &&
272 cpp::is_same_v<InputType, OutputType> &&
273 cpp::is_floating_point_v<InputType>) ||
274 (Operation::BeginUnaryOperationsTwoOutputs < op &&
275 op < Operation::EndUnaryOperationsTwoOutputs &&
276 cpp::is_floating_point_v<InputType> &&
277 cpp::is_same_v<OutputType, BinaryOutput<InputType>>) ||
278 (Operation::BeginBinaryOperationsSingleOutput < op &&
279 op < Operation::EndBinaryOperationsSingleOutput &&
280 cpp::is_floating_point_v<OutputType> &&
281 cpp::is_same_v<InputType, BinaryInput<OutputType>>) ||
282 (Operation::BeginBinaryOperationsTwoOutputs < op &&
283 op < Operation::EndBinaryOperationsTwoOutputs &&
284 internal::AreMatchingBinaryInputAndBinaryOutput<InputType,
285 OutputType>::VALUE) ||
286 (Operation::BeginTernaryOperationsSingleOuput < op &&
287 op < Operation::EndTernaryOperationsSingleOutput &&
288 cpp::is_floating_point_v<OutputType> &&
289 cpp::is_same_v<InputType, TernaryInput<OutputType>>);
292 template <Operation op, typename InputType, typename OutputType>
293 __attribute__((no_sanitize("address"))) cpp::enable_if_t<
294 is_valid_operation<op, InputType, OutputType>(),
295 internal::MPFRMatcher<op, /*is_silent*/ false, InputType, OutputType>>
296 get_mpfr_matcher(InputType input, OutputType output_unused,
297 double ulp_tolerance, RoundingMode rounding) {
298 return internal::MPFRMatcher<op, /*is_silent*/ false, InputType, OutputType>(
299 input, ulp_tolerance, rounding);
302 template <Operation op, typename InputType, typename OutputType>
303 __attribute__((no_sanitize("address"))) cpp::enable_if_t<
304 is_valid_operation<op, InputType, OutputType>(),
305 internal::MPFRMatcher<op, /*is_silent*/ true, InputType, OutputType>>
306 get_silent_mpfr_matcher(InputType input, OutputType output_unused,
307 double ulp_tolerance, RoundingMode rounding) {
308 return internal::MPFRMatcher<op, /*is_silent*/ true, InputType, OutputType>(
309 input, ulp_tolerance, rounding);
312 template <typename T> T round(T x, RoundingMode mode);
314 template <typename T> bool round_to_long(T x, long &result);
315 template <typename T> bool round_to_long(T x, RoundingMode mode, long &result);
317 } // namespace mpfr
318 } // namespace testing
319 } // namespace LIBC_NAMESPACE
321 // GET_MPFR_DUMMY_ARG is going to be added to the end of GET_MPFR_MACRO as a
322 // simple way to avoid the compiler warning `gnu-zero-variadic-macro-arguments`.
323 #define GET_MPFR_DUMMY_ARG(...) 0
325 #define GET_MPFR_MACRO(__1, __2, __3, __4, __5, __NAME, ...) __NAME
327 #define EXPECT_MPFR_MATCH_DEFAULT(op, input, match_value, ulp_tolerance) \
328 EXPECT_THAT(match_value, \
329 LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
330 input, match_value, ulp_tolerance, \
331 LIBC_NAMESPACE::testing::mpfr::RoundingMode::Nearest))
333 #define EXPECT_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
334 rounding) \
335 EXPECT_THAT(match_value, \
336 LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
337 input, match_value, ulp_tolerance, rounding))
339 #define EXPECT_MPFR_MATCH(...) \
340 GET_MPFR_MACRO(__VA_ARGS__, EXPECT_MPFR_MATCH_ROUNDING, \
341 EXPECT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG) \
342 (__VA_ARGS__)
344 #define TEST_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
345 rounding) \
346 LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>(input, match_value, \
347 ulp_tolerance, rounding) \
348 .match(match_value)
350 #define TEST_MPFR_MATCH(...) \
351 GET_MPFR_MACRO(__VA_ARGS__, TEST_MPFR_MATCH_ROUNDING, \
352 EXPECT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG) \
353 (__VA_ARGS__)
355 #define EXPECT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \
357 namespace mpfr = LIBC_NAMESPACE::testing::mpfr; \
358 mpfr::ForceRoundingMode __r1(mpfr::RoundingMode::Nearest); \
359 if (__r1.success) { \
360 EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
361 mpfr::RoundingMode::Nearest); \
363 mpfr::ForceRoundingMode __r2(mpfr::RoundingMode::Upward); \
364 if (__r2.success) { \
365 EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
366 mpfr::RoundingMode::Upward); \
368 mpfr::ForceRoundingMode __r3(mpfr::RoundingMode::Downward); \
369 if (__r3.success) { \
370 EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
371 mpfr::RoundingMode::Downward); \
373 mpfr::ForceRoundingMode __r4(mpfr::RoundingMode::TowardZero); \
374 if (__r4.success) { \
375 EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
376 mpfr::RoundingMode::TowardZero); \
380 #define TEST_MPFR_MATCH_ROUNDING_SILENTLY(op, input, match_value, \
381 ulp_tolerance, rounding) \
382 LIBC_NAMESPACE::testing::mpfr::get_silent_mpfr_matcher<op>( \
383 input, match_value, ulp_tolerance, rounding) \
384 .match(match_value)
386 #define ASSERT_MPFR_MATCH_DEFAULT(op, input, match_value, ulp_tolerance) \
387 ASSERT_THAT(match_value, \
388 LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
389 input, match_value, ulp_tolerance, \
390 LIBC_NAMESPACE::testing::mpfr::RoundingMode::Nearest))
392 #define ASSERT_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
393 rounding) \
394 ASSERT_THAT(match_value, \
395 LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
396 input, match_value, ulp_tolerance, rounding))
398 #define ASSERT_MPFR_MATCH(...) \
399 GET_MPFR_MACRO(__VA_ARGS__, ASSERT_MPFR_MATCH_ROUNDING, \
400 ASSERT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG) \
401 (__VA_ARGS__)
403 #define ASSERT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \
405 namespace mpfr = LIBC_NAMESPACE::testing::mpfr; \
406 mpfr::ForceRoundingMode __r1(mpfr::RoundingMode::Nearest); \
407 if (__r1.success) { \
408 ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
409 mpfr::RoundingMode::Nearest); \
411 mpfr::ForceRoundingMode __r2(mpfr::RoundingMode::Upward); \
412 if (__r2.success) { \
413 ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
414 mpfr::RoundingMode::Upward); \
416 mpfr::ForceRoundingMode __r3(mpfr::RoundingMode::Downward); \
417 if (__r3.success) { \
418 ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
419 mpfr::RoundingMode::Downward); \
421 mpfr::ForceRoundingMode __r4(mpfr::RoundingMode::TowardZero); \
422 if (__r4.success) { \
423 ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
424 mpfr::RoundingMode::TowardZero); \
428 #endif // LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H