112779eddSTue Ly //===-- Common utility class for differential analysis --------------------===//
212779eddSTue Ly //
312779eddSTue Ly // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
412779eddSTue Ly // See https://llvm.org/LICENSE.txt for license information.
512779eddSTue Ly // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
612779eddSTue Ly //
712779eddSTue Ly //===----------------------------------------------------------------------===//
812779eddSTue Ly 
912779eddSTue Ly #include "src/__support/FPUtil/FPBits.h"
1012779eddSTue Ly #include "utils/testutils/StreamWrapper.h"
1112779eddSTue Ly #include "utils/testutils/Timer.h"
1212779eddSTue Ly 
1312779eddSTue Ly namespace __llvm_libc {
1412779eddSTue Ly namespace testing {
1512779eddSTue Ly 
1612779eddSTue Ly template <typename T> class BinaryOpSingleOutputDiff {
1712779eddSTue Ly   using FPBits = fputil::FPBits<T>;
1812779eddSTue Ly   using UIntType = typename FPBits::UIntType;
1912779eddSTue Ly   static constexpr UIntType MSBIT = UIntType(1) << (8 * sizeof(UIntType) - 1);
2012779eddSTue Ly   static constexpr UIntType UINTMAX = (MSBIT - 1) + MSBIT;
2112779eddSTue Ly 
2212779eddSTue Ly public:
2312779eddSTue Ly   typedef T Func(T, T);
2412779eddSTue Ly 
run_diff_in_range(Func myFunc,Func otherFunc,UIntType startingBit,UIntType endingBit,UIntType N,testutils::OutputFileStream & log)25*76b57ef8SKirill Okhotnikov   static uint64_t run_diff_in_range(Func myFunc, Func otherFunc,
26*76b57ef8SKirill Okhotnikov                                     UIntType startingBit, UIntType endingBit,
27*76b57ef8SKirill Okhotnikov                                     UIntType N,
28*76b57ef8SKirill Okhotnikov                                     testutils::OutputFileStream &log) {
29*76b57ef8SKirill Okhotnikov     uint64_t result = 0;
30*76b57ef8SKirill Okhotnikov     if (endingBit < startingBit) {
31*76b57ef8SKirill Okhotnikov       return result;
32*76b57ef8SKirill Okhotnikov     }
33*76b57ef8SKirill Okhotnikov 
34*76b57ef8SKirill Okhotnikov     UIntType step = (endingBit - startingBit) / N;
35*76b57ef8SKirill Okhotnikov     for (UIntType bitsX = startingBit, bitsY = endingBit;;
36*76b57ef8SKirill Okhotnikov          bitsX += step, bitsY -= step) {
37*76b57ef8SKirill Okhotnikov       T x = T(FPBits(bitsX));
38*76b57ef8SKirill Okhotnikov       T y = T(FPBits(bitsY));
39*76b57ef8SKirill Okhotnikov       FPBits myBits = FPBits(myFunc(x, y));
40*76b57ef8SKirill Okhotnikov       FPBits otherBits = FPBits(otherFunc(x, y));
41*76b57ef8SKirill Okhotnikov       if (myBits.uintval() != otherBits.uintval()) {
42*76b57ef8SKirill Okhotnikov         result++;
43*76b57ef8SKirill Okhotnikov         log << "       Input: " << bitsX << ", " << bitsY << " (" << x << ", "
44*76b57ef8SKirill Okhotnikov             << y << ")\n"
45*76b57ef8SKirill Okhotnikov             << "   My result: " << myBits.uintval() << " (" << myBits.get_val()
46*76b57ef8SKirill Okhotnikov             << ")\n"
47*76b57ef8SKirill Okhotnikov             << "Other result: " << otherBits.uintval() << " ("
48*76b57ef8SKirill Okhotnikov             << otherBits.get_val() << ")\n"
49*76b57ef8SKirill Okhotnikov             << '\n';
50*76b57ef8SKirill Okhotnikov       }
51*76b57ef8SKirill Okhotnikov 
52*76b57ef8SKirill Okhotnikov       if (endingBit - bitsX < step) {
53*76b57ef8SKirill Okhotnikov         break;
54*76b57ef8SKirill Okhotnikov       }
55*76b57ef8SKirill Okhotnikov     }
56*76b57ef8SKirill Okhotnikov     return result;
57*76b57ef8SKirill Okhotnikov   }
58*76b57ef8SKirill Okhotnikov 
run_perf_in_range(Func myFunc,Func otherFunc,UIntType startingBit,UIntType endingBit,UIntType N,testutils::OutputFileStream & log)5912779eddSTue Ly   static void run_perf_in_range(Func myFunc, Func otherFunc,
6012779eddSTue Ly                                 UIntType startingBit, UIntType endingBit,
6112779eddSTue Ly                                 UIntType N, testutils::OutputFileStream &log) {
6212779eddSTue Ly     auto runner = [=](Func func) {
6312779eddSTue Ly       volatile T result;
6412779eddSTue Ly       if (endingBit < startingBit) {
6512779eddSTue Ly         return;
6612779eddSTue Ly       }
6712779eddSTue Ly 
6812779eddSTue Ly       UIntType step = (endingBit - startingBit) / N;
6912779eddSTue Ly       for (UIntType bitsX = startingBit, bitsY = endingBit;;
7012779eddSTue Ly            bitsX += step, bitsY -= step) {
7112779eddSTue Ly         T x = T(FPBits(bitsX));
7212779eddSTue Ly         T y = T(FPBits(bitsY));
7312779eddSTue Ly         result = func(x, y);
7412779eddSTue Ly         if (endingBit - bitsX < step) {
7512779eddSTue Ly           break;
7612779eddSTue Ly         }
7712779eddSTue Ly       }
7812779eddSTue Ly     };
7912779eddSTue Ly 
8012779eddSTue Ly     Timer timer;
8112779eddSTue Ly     timer.start();
8212779eddSTue Ly     runner(myFunc);
8312779eddSTue Ly     timer.stop();
8412779eddSTue Ly 
8512779eddSTue Ly     double my_average = static_cast<double>(timer.nanoseconds()) / N;
8612779eddSTue Ly     log << "-- My function --\n";
8712779eddSTue Ly     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
8812779eddSTue Ly     log << "     Average runtime : " << my_average << " ns/op \n";
8912779eddSTue Ly     log << "     Ops per second  : "
9012779eddSTue Ly         << static_cast<uint64_t>(1'000'000'000.0 / my_average) << " op/s \n";
9112779eddSTue Ly 
9212779eddSTue Ly     timer.start();
9312779eddSTue Ly     runner(otherFunc);
9412779eddSTue Ly     timer.stop();
9512779eddSTue Ly 
9612779eddSTue Ly     double other_average = static_cast<double>(timer.nanoseconds()) / N;
9712779eddSTue Ly     log << "-- Other function --\n";
9812779eddSTue Ly     log << "     Total time      : " << timer.nanoseconds() << " ns \n";
9912779eddSTue Ly     log << "     Average runtime : " << other_average << " ns/op \n";
10012779eddSTue Ly     log << "     Ops per second  : "
10112779eddSTue Ly         << static_cast<uint64_t>(1'000'000'000.0 / other_average) << " op/s \n";
10212779eddSTue Ly 
10312779eddSTue Ly     log << "-- Average runtime ratio --\n";
10412779eddSTue Ly     log << "     Mine / Other's  : " << my_average / other_average << " \n";
10512779eddSTue Ly   }
10612779eddSTue Ly 
run_perf(Func myFunc,Func otherFunc,const char * logFile)10712779eddSTue Ly   static void run_perf(Func myFunc, Func otherFunc, const char *logFile) {
10812779eddSTue Ly     testutils::OutputFileStream log(logFile);
10912779eddSTue Ly     log << " Performance tests with inputs in denormal range:\n";
11012779eddSTue Ly     run_perf_in_range(myFunc, otherFunc, /* startingBit= */ UIntType(0),
11112779eddSTue Ly                       /* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log);
11212779eddSTue Ly     log << "\n Performance tests with inputs in normal range:\n";
11312779eddSTue Ly     run_perf_in_range(myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL,
11412779eddSTue Ly                       /* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log);
115f1ec99f9STue Ly     log << "\n Performance tests with inputs in normal range with exponents "
116f1ec99f9STue Ly            "close to each other:\n";
117f1ec99f9STue Ly     run_perf_in_range(
118f1ec99f9STue Ly         myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(),
119f1ec99f9STue Ly         /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log);
12012779eddSTue Ly   }
121*76b57ef8SKirill Okhotnikov 
run_diff(Func myFunc,Func otherFunc,const char * logFile)122*76b57ef8SKirill Okhotnikov   static void run_diff(Func myFunc, Func otherFunc, const char *logFile) {
123*76b57ef8SKirill Okhotnikov     uint64_t diffCount = 0;
124*76b57ef8SKirill Okhotnikov     testutils::OutputFileStream log(logFile);
125*76b57ef8SKirill Okhotnikov     log << " Diff tests with inputs in denormal range:\n";
126*76b57ef8SKirill Okhotnikov     diffCount += run_diff_in_range(
127*76b57ef8SKirill Okhotnikov         myFunc, otherFunc, /* startingBit= */ UIntType(0),
128*76b57ef8SKirill Okhotnikov         /* endingBit= */ FPBits::MAX_SUBNORMAL, 1'000'001, log);
129*76b57ef8SKirill Okhotnikov     log << "\n Diff tests with inputs in normal range:\n";
130*76b57ef8SKirill Okhotnikov     diffCount += run_diff_in_range(
131*76b57ef8SKirill Okhotnikov         myFunc, otherFunc, /* startingBit= */ FPBits::MIN_NORMAL,
132*76b57ef8SKirill Okhotnikov         /* endingBit= */ FPBits::MAX_NORMAL, 100'000'001, log);
133*76b57ef8SKirill Okhotnikov     log << "\n Diff tests with inputs in normal range with exponents "
134*76b57ef8SKirill Okhotnikov            "close to each other:\n";
135*76b57ef8SKirill Okhotnikov     diffCount += run_diff_in_range(
136*76b57ef8SKirill Okhotnikov         myFunc, otherFunc, /* startingBit= */ FPBits(T(0x1.0p-10)).uintval(),
137*76b57ef8SKirill Okhotnikov         /* endingBit= */ FPBits(T(0x1.0p+10)).uintval(), 10'000'001, log);
138*76b57ef8SKirill Okhotnikov 
139*76b57ef8SKirill Okhotnikov     log << "Total number of differing results: " << diffCount << '\n';
140*76b57ef8SKirill Okhotnikov   }
14112779eddSTue Ly };
14212779eddSTue Ly 
14312779eddSTue Ly } // namespace testing
14412779eddSTue Ly } // namespace __llvm_libc
14512779eddSTue Ly 
14612779eddSTue Ly #define BINARY_OP_SINGLE_OUTPUT_DIFF(T, myFunc, otherFunc, filename)           \
14712779eddSTue Ly   int main() {                                                                 \
14812779eddSTue Ly     __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_diff(               \
14912779eddSTue Ly         &myFunc, &otherFunc, filename);                                        \
15012779eddSTue Ly     return 0;                                                                  \
15112779eddSTue Ly   }
15212779eddSTue Ly 
15312779eddSTue Ly #define BINARY_OP_SINGLE_OUTPUT_PERF(T, myFunc, otherFunc, filename)           \
15412779eddSTue Ly   int main() {                                                                 \
15512779eddSTue Ly     __llvm_libc::testing::BinaryOpSingleOutputDiff<T>::run_perf(               \
15612779eddSTue Ly         &myFunc, &otherFunc, filename);                                        \
15712779eddSTue Ly     return 0;                                                                  \
15812779eddSTue Ly   }
159