brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.5 KiB · 3a8f534 Raw
492 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_LIBC_UTILS_MPFRWRAPPER_MPFRUTILS_H10#define LLVM_LIBC_UTILS_MPFRWRAPPER_MPFRUTILS_H11 12#include "hdr/stdint_proxy.h"13#include "src/__support/CPP/type_traits.h"14#include "src/__support/macros/config.h"15#include "test/UnitTest/RoundingModeUtils.h"16#include "test/UnitTest/Test.h"17 18namespace LIBC_NAMESPACE_DECL {19namespace testing {20namespace mpfr {21 22enum class Operation : int {23  // Operations which take a single floating point number as input24  // and produce a single floating point number as output. The input25  // and output floating point numbers are of the same kind.26  BeginUnaryOperationsSingleOutput,27  Abs,28  Acos,29  Acosh,30  Acospi,31  Asin,32  Asinh,33  Asinpi,34  Atan,35  Atanh,36  Atanpi,37  Cbrt,38  Ceil,39  Cos,40  Cosh,41  Cospi,42  Erf,43  Exp,44  Exp2,45  Exp2m1,46  Exp10,47  Exp10m1,48  Expm1,49  Floor,50  Log,51  Log2,52  Log10,53  Log1p,54  Mod2PI,55  ModPIOver2,56  ModPIOver4,57  Round,58  RoundEven,59  Rsqrt,60  Sin,61  Sinpi,62  Sinh,63  Sqrt,64  Tan,65  Tanh,66  Tanpi,67  Trunc,68  EndUnaryOperationsSingleOutput,69 70  // Operations which take a single floating point nubmer as input71  // but produce two outputs. The first ouput is a floating point72  // number of the same type as the input. The second output is of type73  // 'int'.74  BeginUnaryOperationsTwoOutputs,75  Frexp, // Floating point output, the first output, is the fractional part.76  EndUnaryOperationsTwoOutputs,77 78  // Operations wich take two floating point nubmers of the same type as79  // input and produce a single floating point number of the same type as80  // output.81  BeginBinaryOperationsSingleOutput,82  Add,83  Atan2,84  Div,85  Fmod,86  Hypot,87  Mul,88  Pow,89  Sub,90  EndBinaryOperationsSingleOutput,91 92  // Operations which take two floating point numbers of the same type as93  // input and produce two outputs. The first output is a floating point number94  // of the same type as the inputs. The second output is of type 'int'.95  BeginBinaryOperationsTwoOutputs,96  RemQuo, // The first output(floating point) is the remainder.97  EndBinaryOperationsTwoOutputs,98 99  // Operations which take three floating point nubmers of the same type as100  // input and produce a single floating point number of the same type as101  // output.102  BeginTernaryOperationsSingleOuput,103  Fma,104  EndTernaryOperationsSingleOutput,105};106 107using LIBC_NAMESPACE::fputil::testing::ForceRoundingMode;108using LIBC_NAMESPACE::fputil::testing::RoundingMode;109 110template <typename T> struct BinaryInput {111  static_assert(112      LIBC_NAMESPACE::cpp::is_floating_point_v<T>,113      "Template parameter of BinaryInput must be a floating point type.");114 115  using Type = T;116  T x, y;117};118 119template <typename T> struct TernaryInput {120  static_assert(121      LIBC_NAMESPACE::cpp::is_floating_point_v<T>,122      "Template parameter of TernaryInput must be a floating point type.");123 124  using Type = T;125  T x, y, z;126};127 128template <typename T> struct BinaryOutput {129  T f;130  int i;131};132 133namespace internal {134 135template <typename T1, typename T2>136struct AreMatchingBinaryInputAndBinaryOutput {137  static constexpr bool VALUE = false;138};139 140template <typename T>141struct AreMatchingBinaryInputAndBinaryOutput<BinaryInput<T>, BinaryOutput<T>> {142  static constexpr bool VALUE = cpp::is_floating_point_v<T>;143};144 145template <typename T> struct IsBinaryInput {146  static constexpr bool VALUE = false;147};148 149template <typename T> struct IsBinaryInput<BinaryInput<T>> {150  static constexpr bool VALUE = true;151};152 153template <typename T> struct IsTernaryInput {154  static constexpr bool VALUE = false;155};156 157template <typename T> struct IsTernaryInput<TernaryInput<T>> {158  static constexpr bool VALUE = true;159};160 161template <typename T> struct MakeScalarInput : cpp::type_identity<T> {};162 163template <typename T>164struct MakeScalarInput<BinaryInput<T>> : cpp::type_identity<T> {};165 166template <typename T>167struct MakeScalarInput<TernaryInput<T>> : cpp::type_identity<T> {};168 169template <typename InputType, typename OutputType>170bool compare_unary_operation_single_output(Operation op, InputType input,171                                           OutputType libc_output,172                                           double ulp_tolerance,173                                           RoundingMode rounding);174template <typename T>175bool compare_unary_operation_two_outputs(Operation op, T input,176                                         const BinaryOutput<T> &libc_output,177                                         double ulp_tolerance,178                                         RoundingMode rounding);179template <typename T>180bool compare_binary_operation_two_outputs(Operation op,181                                          const BinaryInput<T> &input,182                                          const BinaryOutput<T> &libc_output,183                                          double ulp_tolerance,184                                          RoundingMode rounding);185 186template <typename InputType, typename OutputType>187bool compare_binary_operation_one_output(Operation op,188                                         const BinaryInput<InputType> &input,189                                         OutputType libc_output,190                                         double ulp_tolerance,191                                         RoundingMode rounding);192 193template <typename InputType, typename OutputType>194bool compare_ternary_operation_one_output(Operation op,195                                          const TernaryInput<InputType> &input,196                                          OutputType libc_output,197                                          double ulp_tolerance,198                                          RoundingMode rounding);199 200template <typename InputType, typename OutputType>201void explain_unary_operation_single_output_error(Operation op, InputType input,202                                                 OutputType match_value,203                                                 double ulp_tolerance,204                                                 RoundingMode rounding);205template <typename T>206void explain_unary_operation_two_outputs_error(207    Operation op, T input, const BinaryOutput<T> &match_value,208    double ulp_tolerance, RoundingMode rounding);209template <typename T>210void explain_binary_operation_two_outputs_error(211    Operation op, const BinaryInput<T> &input,212    const BinaryOutput<T> &match_value, double ulp_tolerance,213    RoundingMode rounding);214 215template <typename InputType, typename OutputType>216void explain_binary_operation_one_output_error(217    Operation op, const BinaryInput<InputType> &input, OutputType match_value,218    double ulp_tolerance, RoundingMode rounding);219 220template <typename InputType, typename OutputType>221void explain_ternary_operation_one_output_error(222    Operation op, const TernaryInput<InputType> &input, OutputType match_value,223    double ulp_tolerance, RoundingMode rounding);224 225template <Operation op, bool silent, typename InputType, typename OutputType>226class MPFRMatcher : public testing::Matcher<OutputType> {227  InputType input;228  OutputType match_value;229  double ulp_tolerance;230  RoundingMode rounding;231 232public:233  MPFRMatcher(InputType testInput, double ulp_tolerance, RoundingMode rounding)234      : input(testInput), ulp_tolerance(ulp_tolerance), rounding(rounding) {}235 236  bool match(OutputType libcResult) {237    match_value = libcResult;238    return match(input, match_value);239  }240 241  // This method is marked with NOLINT because the name `explainError` does not242  // conform to the coding style.243  void explainError() override { // NOLINT244    explain_error(input, match_value);245  }246 247  // Whether the `explainError` step is skipped or not.248  bool is_silent() const override { return silent; }249 250private:251  template <typename InType, typename OutType>252  bool match(InType in, OutType out) {253    return compare_unary_operation_single_output(op, in, out, ulp_tolerance,254                                                 rounding);255  }256 257  template <typename T> bool match(T in, const BinaryOutput<T> &out) {258    return compare_unary_operation_two_outputs(op, in, out, ulp_tolerance,259                                               rounding);260  }261 262  template <typename T, typename U>263  bool match(const BinaryInput<T> &in, U out) {264    return compare_binary_operation_one_output(op, in, out, ulp_tolerance,265                                               rounding);266  }267 268  template <typename T>269  bool match(BinaryInput<T> in, const BinaryOutput<T> &out) {270    return compare_binary_operation_two_outputs(op, in, out, ulp_tolerance,271                                                rounding);272  }273 274  template <typename InType, typename OutType>275  bool match(const TernaryInput<InType> &in, OutType out) {276    return compare_ternary_operation_one_output(op, in, out, ulp_tolerance,277                                                rounding);278  }279 280  template <typename InType, typename OutType>281  void explain_error(InType in, OutType out) {282    explain_unary_operation_single_output_error(op, in, out, ulp_tolerance,283                                                rounding);284  }285 286  template <typename T> void explain_error(T in, const BinaryOutput<T> &out) {287    explain_unary_operation_two_outputs_error(op, in, out, ulp_tolerance,288                                              rounding);289  }290 291  template <typename T>292  void explain_error(const BinaryInput<T> &in, const BinaryOutput<T> &out) {293    explain_binary_operation_two_outputs_error(op, in, out, ulp_tolerance,294                                               rounding);295  }296 297  template <typename T, typename U>298  void explain_error(const BinaryInput<T> &in, U out) {299    explain_binary_operation_one_output_error(op, in, out, ulp_tolerance,300                                              rounding);301  }302 303  template <typename InType, typename OutType>304  void explain_error(const TernaryInput<InType> &in, OutType out) {305    explain_ternary_operation_one_output_error(op, in, out, ulp_tolerance,306                                               rounding);307  }308};309 310} // namespace internal311 312// Return true if the input and ouput types for the operation op are valid313// types.314template <Operation op, typename InputType, typename OutputType>315constexpr bool is_valid_operation() {316  constexpr bool IS_NARROWING_OP =317      (op == Operation::Sqrt && cpp::is_floating_point_v<InputType> &&318       cpp::is_floating_point_v<OutputType> &&319       sizeof(OutputType) <= sizeof(InputType)) ||320      (Operation::BeginBinaryOperationsSingleOutput < op &&321       op < Operation::EndBinaryOperationsSingleOutput &&322       internal::IsBinaryInput<InputType>::VALUE &&323       cpp::is_floating_point_v<324           typename internal::MakeScalarInput<InputType>::type> &&325       cpp::is_floating_point_v<OutputType>) ||326      (op == Operation::Fma && internal::IsTernaryInput<InputType>::VALUE &&327       cpp::is_floating_point_v<328           typename internal::MakeScalarInput<InputType>::type> &&329       cpp::is_floating_point_v<OutputType>);330  if (IS_NARROWING_OP)331    return true;332  return (Operation::BeginUnaryOperationsSingleOutput < op &&333          op < Operation::EndUnaryOperationsSingleOutput &&334          cpp::is_same_v<InputType, OutputType> &&335          cpp::is_floating_point_v<InputType>) ||336         (Operation::BeginUnaryOperationsTwoOutputs < op &&337          op < Operation::EndUnaryOperationsTwoOutputs &&338          cpp::is_floating_point_v<InputType> &&339          cpp::is_same_v<OutputType, BinaryOutput<InputType>>) ||340         (Operation::BeginBinaryOperationsSingleOutput < op &&341          op < Operation::EndBinaryOperationsSingleOutput &&342          cpp::is_floating_point_v<OutputType> &&343          cpp::is_same_v<InputType, BinaryInput<OutputType>>) ||344         (Operation::BeginBinaryOperationsTwoOutputs < op &&345          op < Operation::EndBinaryOperationsTwoOutputs &&346          internal::AreMatchingBinaryInputAndBinaryOutput<InputType,347                                                          OutputType>::VALUE) ||348         (Operation::BeginTernaryOperationsSingleOuput < op &&349          op < Operation::EndTernaryOperationsSingleOutput &&350          cpp::is_floating_point_v<OutputType> &&351          cpp::is_same_v<InputType, TernaryInput<OutputType>>);352}353 354template <Operation op, typename InputType, typename OutputType>355__attribute__((no_sanitize("address"))) cpp::enable_if_t<356    is_valid_operation<op, InputType, OutputType>(),357    internal::MPFRMatcher<op, /*is_silent*/ false, InputType, OutputType>>358get_mpfr_matcher(InputType input, [[maybe_unused]] OutputType output_unused,359                 double ulp_tolerance, RoundingMode rounding) {360  return internal::MPFRMatcher<op, /*is_silent*/ false, InputType, OutputType>(361      input, ulp_tolerance, rounding);362}363 364template <Operation op, typename InputType, typename OutputType>365__attribute__((no_sanitize("address"))) cpp::enable_if_t<366    is_valid_operation<op, InputType, OutputType>(),367    internal::MPFRMatcher<op, /*is_silent*/ true, InputType, OutputType>>368get_silent_mpfr_matcher(InputType input,369                        [[maybe_unused]] OutputType output_unused,370                        double ulp_tolerance, RoundingMode rounding) {371  return internal::MPFRMatcher<op, /*is_silent*/ true, InputType, OutputType>(372      input, ulp_tolerance, rounding);373}374 375template <typename T> T round(T x, RoundingMode mode);376 377template <typename T> bool round_to_long(T x, long &result);378template <typename T> bool round_to_long(T x, RoundingMode mode, long &result);379 380} // namespace mpfr381} // namespace testing382} // namespace LIBC_NAMESPACE_DECL383 384// GET_MPFR_DUMMY_ARG is going to be added to the end of GET_MPFR_MACRO as a385// simple way to avoid the compiler warning `gnu-zero-variadic-macro-arguments`.386#define GET_MPFR_DUMMY_ARG(...) 0387 388#define GET_MPFR_MACRO(__1, __2, __3, __4, __5, __NAME, ...) __NAME389 390#define EXPECT_MPFR_MATCH_DEFAULT(op, input, match_value, ulp_tolerance)       \391  EXPECT_THAT(match_value,                                                     \392              LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>(             \393                  input, match_value, ulp_tolerance,                           \394                  LIBC_NAMESPACE::testing::mpfr::RoundingMode::Nearest))395 396#define EXPECT_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance,      \397                                   rounding)                                   \398  EXPECT_THAT(match_value,                                                     \399              LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>(             \400                  input, match_value, ulp_tolerance, rounding))401 402#define EXPECT_MPFR_MATCH(...)                                                 \403  GET_MPFR_MACRO(__VA_ARGS__, EXPECT_MPFR_MATCH_ROUNDING,                      \404                 EXPECT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG)                \405  (__VA_ARGS__)406 407#define TEST_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance,        \408                                 rounding)                                     \409  LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>(input, match_value,      \410                                                      ulp_tolerance, rounding) \411      .match(match_value)412 413#define TEST_MPFR_MATCH(...)                                                   \414  GET_MPFR_MACRO(__VA_ARGS__, TEST_MPFR_MATCH_ROUNDING,                        \415                 EXPECT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG)                \416  (__VA_ARGS__)417 418#define EXPECT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance)  \419  {                                                                            \420    namespace mpfr = LIBC_NAMESPACE::testing::mpfr;                            \421    mpfr::ForceRoundingMode __r1(mpfr::RoundingMode::Nearest);                 \422    if (__r1.success) {                                                        \423      EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance,                 \424                        mpfr::RoundingMode::Nearest);                          \425    }                                                                          \426    mpfr::ForceRoundingMode __r2(mpfr::RoundingMode::Upward);                  \427    if (__r2.success) {                                                        \428      EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance,                 \429                        mpfr::RoundingMode::Upward);                           \430    }                                                                          \431    mpfr::ForceRoundingMode __r3(mpfr::RoundingMode::Downward);                \432    if (__r3.success) {                                                        \433      EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance,                 \434                        mpfr::RoundingMode::Downward);                         \435    }                                                                          \436    mpfr::ForceRoundingMode __r4(mpfr::RoundingMode::TowardZero);              \437    if (__r4.success) {                                                        \438      EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance,                 \439                        mpfr::RoundingMode::TowardZero);                       \440    }                                                                          \441  }442 443#define TEST_MPFR_MATCH_ROUNDING_SILENTLY(op, input, match_value,              \444                                          ulp_tolerance, rounding)             \445  LIBC_NAMESPACE::testing::mpfr::get_silent_mpfr_matcher<op>(                  \446      input, match_value, ulp_tolerance, rounding)                             \447      .match(match_value)448 449#define ASSERT_MPFR_MATCH_DEFAULT(op, input, match_value, ulp_tolerance)       \450  ASSERT_THAT(match_value,                                                     \451              LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>(             \452                  input, match_value, ulp_tolerance,                           \453                  LIBC_NAMESPACE::testing::mpfr::RoundingMode::Nearest))454 455#define ASSERT_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance,      \456                                   rounding)                                   \457  ASSERT_THAT(match_value,                                                     \458              LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>(             \459                  input, match_value, ulp_tolerance, rounding))460 461#define ASSERT_MPFR_MATCH(...)                                                 \462  GET_MPFR_MACRO(__VA_ARGS__, ASSERT_MPFR_MATCH_ROUNDING,                      \463                 ASSERT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG)                \464  (__VA_ARGS__)465 466#define ASSERT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance)  \467  {                                                                            \468    namespace mpfr = LIBC_NAMESPACE::testing::mpfr;                            \469    mpfr::ForceRoundingMode __r1(mpfr::RoundingMode::Nearest);                 \470    if (__r1.success) {                                                        \471      ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance,                 \472                        mpfr::RoundingMode::Nearest);                          \473    }                                                                          \474    mpfr::ForceRoundingMode __r2(mpfr::RoundingMode::Upward);                  \475    if (__r2.success) {                                                        \476      ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance,                 \477                        mpfr::RoundingMode::Upward);                           \478    }                                                                          \479    mpfr::ForceRoundingMode __r3(mpfr::RoundingMode::Downward);                \480    if (__r3.success) {                                                        \481      ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance,                 \482                        mpfr::RoundingMode::Downward);                         \483    }                                                                          \484    mpfr::ForceRoundingMode __r4(mpfr::RoundingMode::TowardZero);              \485    if (__r4.success) {                                                        \486      ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance,                 \487                        mpfr::RoundingMode::TowardZero);                       \488    }                                                                          \489  }490 491#endif // LLVM_LIBC_UTILS_MPFRWRAPPER_MPFRUTILS_H492