brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.7 KiB · 322d774 Raw
273 lines · c
1//===-- Exhaustive test template for math functions -------------*- 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#include "src/__support/CPP/type_traits.h"10#include "src/__support/FPUtil/FPBits.h"11#include "src/__support/macros/properties/types.h"12#include "test/UnitTest/FPMatcher.h"13#include "test/UnitTest/Test.h"14#include "utils/MPFRWrapper/MPFRUtils.h"15 16#include <atomic>17#include <functional>18#include <iostream>19#include <mutex>20#include <sstream>21#include <thread>22#include <vector>23 24// To test exhaustively for inputs in the range [start, stop) in parallel:25// 1. Define a Checker class with:26//    - FloatType: define floating point type to be used.27//    - FPBits: fputil::FPBits<FloatType>.28//    - StorageType: define bit type for the corresponding floating point type.29//    - uint64_t check(start, stop, rounding_mode): a method to test in given30//          range for a given rounding mode, which returns the number of31//          failures.32// 2. Use LlvmLibcExhaustiveMathTest<Checker> class33// 3. Call: test_full_range(start, stop, nthreads, rounding)34//       or test_full_range_all_roundings(start, stop).35// * For single input single output math function, use the convenient template:36//   LlvmLibcUnaryOpExhaustiveMathTest<FloatType, Op, Func>.37namespace mpfr = LIBC_NAMESPACE::testing::mpfr;38 39template <typename OutType, typename InType = OutType>40using UnaryOp = OutType(InType);41 42template <typename OutType, typename InType, mpfr::Operation Op,43          UnaryOp<OutType, InType> Func, unsigned Tolerance = 0>44struct UnaryOpChecker : public virtual LIBC_NAMESPACE::testing::Test {45  using FloatType = InType;46  using FPBits = LIBC_NAMESPACE::fputil::FPBits<FloatType>;47  using StorageType = typename FPBits::StorageType;48 49  // Check in a range, return the number of failures.50  uint64_t check(StorageType start, StorageType stop,51                 mpfr::RoundingMode rounding) {52    mpfr::ForceRoundingMode r(rounding);53    if (!r.success)54      return (stop > start);55    StorageType bits = start;56    uint64_t failed = 0;57    do {58      FPBits xbits(bits);59      FloatType x = xbits.get_val();60      bool correct = TEST_MPFR_MATCH_ROUNDING_SILENTLY(61          Op, x, Func(x), static_cast<double>(Tolerance) + 0.5, rounding);62      failed += (!correct);63      // Uncomment to print out failed values.64      if (!correct) {65        EXPECT_MPFR_MATCH_ROUNDING(Op, x, Func(x), 0.5, rounding);66      }67    } while (bits++ < stop);68    return failed;69  }70};71 72template <typename OutType, typename InType = OutType>73using BinaryOp = OutType(InType, InType);74 75template <typename OutType, typename InType, mpfr::Operation Op,76          BinaryOp<OutType, InType> Func>77struct BinaryOpChecker : public virtual LIBC_NAMESPACE::testing::Test {78  using FloatType = InType;79  using FPBits = LIBC_NAMESPACE::fputil::FPBits<FloatType>;80  using StorageType = typename FPBits::StorageType;81 82  // Check in a range, return the number of failures.83  uint64_t check(StorageType x_start, StorageType x_stop, StorageType y_start,84                 StorageType y_stop, mpfr::RoundingMode rounding) {85    mpfr::ForceRoundingMode r(rounding);86    if (!r.success)87      return x_stop > x_start || y_stop > y_start;88    StorageType xbits = x_start;89    uint64_t failed = 0;90    do {91      FloatType x = FPBits(xbits).get_val();92      StorageType ybits = y_start;93      do {94        FloatType y = FPBits(ybits).get_val();95        mpfr::BinaryInput<FloatType> input{x, y};96        bool correct = TEST_MPFR_MATCH_ROUNDING_SILENTLY(Op, input, Func(x, y),97                                                         0.5, rounding);98        failed += (!correct);99        // Uncomment to print out failed values.100        if (!correct) {101          EXPECT_MPFR_MATCH_ROUNDING(Op, input, Func(x, y), 0.5, rounding);102        }103      } while (ybits++ < y_stop);104    } while (xbits++ < x_stop);105    return failed;106  }107};108 109// Checker class needs inherit from LIBC_NAMESPACE::testing::Test and provide110//   StorageType and check method.111template <typename Checker, size_t Increment = 1 << 20>112struct LlvmLibcExhaustiveMathTest113    : public virtual LIBC_NAMESPACE::testing::Test,114      public Checker {115  using FloatType = typename Checker::FloatType;116  using FPBits = typename Checker::FPBits;117  using StorageType = typename Checker::StorageType;118 119  void explain_failed_range(std::stringstream &msg, StorageType x_begin,120                            StorageType x_end) {121#ifdef LIBC_TYPES_HAS_FLOAT16122    using T = LIBC_NAMESPACE::cpp::conditional_t<123        LIBC_NAMESPACE::cpp::is_same_v<FloatType, float16>, float, FloatType>;124#else125    using T = FloatType;126#endif127 128    msg << x_begin << " to " << x_end << " [0x" << std::hex << x_begin << ", 0x"129        << x_end << "), [" << std::hexfloat130        << static_cast<T>(FPBits(x_begin).get_val()) << ", "131        << static_cast<T>(FPBits(x_end).get_val()) << ")";132  }133 134  void explain_failed_range(std::stringstream &msg, StorageType x_begin,135                            StorageType x_end, StorageType y_begin,136                            StorageType y_end) {137    msg << "x ";138    explain_failed_range(msg, x_begin, x_end);139    msg << ", y ";140    explain_failed_range(msg, y_begin, y_end);141  }142 143  // Break [start, stop) into `nthreads` subintervals and apply *check to each144  // subinterval in parallel.145  template <typename... T>146  void test_full_range(mpfr::RoundingMode rounding, StorageType start,147                       StorageType stop, T... extra_range_bounds) {148    int n_threads = std::thread::hardware_concurrency();149    std::vector<std::thread> thread_list;150    std::mutex mx_cur_val;151    int current_percent = -1;152    StorageType current_value = start;153    std::atomic<uint64_t> failed(0);154 155    for (int i = 0; i < n_threads; ++i) {156      thread_list.emplace_back([&, this]() {157        while (true) {158          StorageType range_begin, range_end;159          int new_percent = -1;160          {161            std::lock_guard<std::mutex> lock(mx_cur_val);162            if (current_value == stop)163              return;164 165            range_begin = current_value;166            if (stop >= Increment && stop - Increment >= current_value) {167              range_end = static_cast<StorageType>(current_value + Increment);168            } else {169              range_end = stop;170            }171            current_value = range_end;172            int pc =173                static_cast<int>(100.0 * (range_end - start) / (stop - start));174            if (current_percent != pc) {175              new_percent = pc;176              current_percent = pc;177            }178          }179          if (new_percent >= 0) {180            std::stringstream msg;181            msg << new_percent << "% is in process     \r";182            std::cout << msg.str() << std::flush;183          }184 185          uint64_t failed_in_range = Checker::check(186              range_begin, range_end, extra_range_bounds..., rounding);187          if (failed_in_range > 0) {188            std::stringstream msg;189            msg << "Test failed for " << std::dec << failed_in_range190                << " inputs in range: ";191            explain_failed_range(msg, range_begin, range_end,192                                 extra_range_bounds...);193            msg << "\n";194            std::cerr << msg.str() << std::flush;195 196            failed.fetch_add(failed_in_range);197          }198        }199      });200    }201 202    for (auto &thread : thread_list) {203      if (thread.joinable()) {204        thread.join();205      }206    }207 208    std::cout << std::endl;209    std::cout << "Test " << ((failed > 0) ? "FAILED" : "PASSED") << std::endl;210    ASSERT_EQ(failed.load(), uint64_t(0));211  }212 213  void test_full_range_all_roundings(StorageType start, StorageType stop) {214    std::cout << "-- Testing for FE_TONEAREST in range [0x" << std::hex << start215              << ", 0x" << stop << ") --" << std::dec << std::endl;216    test_full_range(mpfr::RoundingMode::Nearest, start, stop);217 218    std::cout << "-- Testing for FE_UPWARD in range [0x" << std::hex << start219              << ", 0x" << stop << ") --" << std::dec << std::endl;220    test_full_range(mpfr::RoundingMode::Upward, start, stop);221 222    std::cout << "-- Testing for FE_DOWNWARD in range [0x" << std::hex << start223              << ", 0x" << stop << ") --" << std::dec << std::endl;224    test_full_range(mpfr::RoundingMode::Downward, start, stop);225 226    std::cout << "-- Testing for FE_TOWARDZERO in range [0x" << std::hex227              << start << ", 0x" << stop << ") --" << std::dec << std::endl;228    test_full_range(mpfr::RoundingMode::TowardZero, start, stop);229  }230 231  void test_full_range_all_roundings(StorageType x_start, StorageType x_stop,232                                     StorageType y_start, StorageType y_stop) {233    std::cout << "-- Testing for FE_TONEAREST in x range [0x" << std::hex234              << x_start << ", 0x" << x_stop << "), y range [0x" << y_start235              << ", 0x" << y_stop << ") --" << std::dec << std::endl;236    test_full_range(mpfr::RoundingMode::Nearest, x_start, x_stop, y_start,237                    y_stop);238 239    std::cout << "-- Testing for FE_UPWARD in x range [0x" << std::hex240              << x_start << ", 0x" << x_stop << "), y range [0x" << y_start241              << ", 0x" << y_stop << ") --" << std::dec << std::endl;242    test_full_range(mpfr::RoundingMode::Upward, x_start, x_stop, y_start,243                    y_stop);244 245    std::cout << "-- Testing for FE_DOWNWARD in x range [0x" << std::hex246              << x_start << ", 0x" << x_stop << "), y range [0x" << y_start247              << ", 0x" << y_stop << ") --" << std::dec << std::endl;248    test_full_range(mpfr::RoundingMode::Downward, x_start, x_stop, y_start,249                    y_stop);250 251    std::cout << "-- Testing for FE_TOWARDZERO in x range [0x" << std::hex252              << x_start << ", 0x" << x_stop << "), y range [0x" << y_start253              << ", 0x" << y_stop << ") --" << std::dec << std::endl;254    test_full_range(mpfr::RoundingMode::TowardZero, x_start, x_stop, y_start,255                    y_stop);256  }257};258 259template <typename FloatType, mpfr::Operation Op, UnaryOp<FloatType> Func,260          unsigned Tolerance = 0>261using LlvmLibcUnaryOpExhaustiveMathTest = LlvmLibcExhaustiveMathTest<262    UnaryOpChecker<FloatType, FloatType, Op, Func, Tolerance>>;263 264template <typename OutType, typename InType, mpfr::Operation Op,265          UnaryOp<OutType, InType> Func>266using LlvmLibcUnaryNarrowingOpExhaustiveMathTest =267    LlvmLibcExhaustiveMathTest<UnaryOpChecker<OutType, InType, Op, Func>>;268 269template <typename FloatType, mpfr::Operation Op, BinaryOp<FloatType> Func>270using LlvmLibcBinaryOpExhaustiveMathTest =271    LlvmLibcExhaustiveMathTest<BinaryOpChecker<FloatType, FloatType, Op, Func>,272                               1 << 2>;273