[libc] Add multithreading support for exhaustive testing and MPFRUtils.

Add threading support for exhaustive testing and MPFRUtils.

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D117028
This commit is contained in:
Tue Ly 2022-01-11 10:59:25 -05:00
parent f7c589d3e7
commit 8cd81274ff
8 changed files with 219 additions and 63 deletions

View File

@ -63,6 +63,7 @@ endfunction(get_object_files_for_test)
# HDRS <list of .h files for the test>
# DEPENDS <list of dependencies>
# COMPILE_OPTIONS <list of special compile options for this target>
# LINK_OPTIONS <list of special linking options for this target>
# )
function(add_libc_unittest target_name)
if(NOT LLVM_INCLUDE_TESTS)
@ -71,9 +72,9 @@ function(add_libc_unittest target_name)
cmake_parse_arguments(
"LIBC_UNITTEST"
"" # No optional arguments
"NO_RUN_POSTBUILD" # Optional arguments
"SUITE" # Single value arguments
"SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi-value arguments
"SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;LINK_OPTIONS" # Multi-value arguments
"NO_LIBC_UNITTEST_TEST_MAIN"
${ARGN}
)
@ -140,6 +141,12 @@ function(add_libc_unittest target_name)
endif()
target_link_libraries(${fq_target_name} PRIVATE ${link_object_files})
if(LIBC_UNITTEST_LINK_OPTIONS)
target_link_options(
${fq_target_name}
PRIVATE ${LIBC_UNITTEST_LINK_OPTIONS}
)
endif()
set_target_properties(${fq_target_name}
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
@ -155,11 +162,14 @@ function(add_libc_unittest target_name)
target_link_libraries(${fq_target_name} PRIVATE LibcUnitTest LibcUnitTestMain libc_test_utils)
endif()
add_custom_command(
TARGET ${fq_target_name}
POST_BUILD
COMMAND $<TARGET_FILE:${fq_target_name}>
)
if(NOT LIBC_UNITTEST_NO_RUN_POSTBUILD)
add_custom_command(
TARGET ${fq_target_name}
POST_BUILD
COMMAND $<TARGET_FILE:${fq_target_name}>
)
endif()
if(LIBC_UNITTEST_SUITE)
add_dependencies(
${LIBC_UNITTEST_SUITE}

View File

@ -1,5 +1,15 @@
add_libc_exhaustive_testsuite(libc_math_exhaustive_tests)
add_object_library(
exhaustive_test
HDRS
exhaustive_test.h
SRCS
exhaustive_test.cpp
DEPENDS
libc.src.__support.CPP.standalone_cpp
)
add_fp_unittest(
sqrtf_test
NEED_MPFR
@ -54,13 +64,17 @@ add_fp_unittest(
add_fp_unittest(
logf_test
NO_RUN_POSTBUILD
NEED_MPFR
SUITE
libc_math_exhaustive_tests
SRCS
logf_test.cpp
DEPENDS
.exhaustive_test
libc.include.math
libc.src.math.logf
libc.src.__support.FPUtil.fputil
LINK_OPTIONS
-lpthread
)

View File

@ -0,0 +1,58 @@
//===-- Exhaustive test template for math functions -------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include <fenv.h>
#include <functional>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
#include "exhaustive_test.h"
#include "utils/UnitTest/Test.h"
template <typename T>
void LlvmLibcExhaustiveTest<T>::test_full_range(T start, T stop, int nthreads,
mpfr::RoundingMode rounding) {
std::vector<std::thread> thread_list(nthreads);
T increment = (stop - start - 1) / nthreads + 1;
T begin = start;
T end = start + increment - 1;
for (int i = 0; i < nthreads; ++i) {
thread_list.emplace_back([this, begin, end, rounding]() {
std::stringstream msg;
msg << "-- Testing from " << begin << " to " << end << " [0x" << std::hex
<< begin << ", 0x" << end << ") ..." << std::endl;
std::cout << msg.str();
msg.str("");
check(begin, end, rounding);
msg << "** Finished testing from " << std::dec << begin << " to " << end
<< " [0x" << std::hex << begin << ", 0x" << end << ")" << std::endl;
std::cout << msg.str();
});
begin += increment;
end += increment;
if (end > stop)
end = stop;
}
for (auto &thread : thread_list) {
if (thread.joinable()) {
thread.join();
}
}
}
template void
LlvmLibcExhaustiveTest<uint32_t>::test_full_range(uint32_t, uint32_t, int,
mpfr::RoundingMode);
template void
LlvmLibcExhaustiveTest<uint64_t>::test_full_range(uint64_t, uint64_t, int,
mpfr::RoundingMode);

View File

@ -0,0 +1,26 @@
//===-- Exhaustive test template for math functions -------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "utils/MPFRWrapper/MPFRUtils.h"
#include "utils/UnitTest/Test.h"
// To test exhaustively for inputs in the range [start, stop) in parallel:
// 1. Inherit from LlvmLibcExhaustiveTest class
// 2. Overide the test method: void check(T, T, RoundingMode)
// 4. Call: test_full_range(start, stop, nthreads, rounding)
namespace mpfr = __llvm_libc::testing::mpfr;
template <typename T>
struct LlvmLibcExhaustiveTest : public __llvm_libc::testing::Test {
// Break [start, stop) into `nthreads` subintervals and apply *check to each
// subinterval in parallel.
void test_full_range(T start, T stop, int nthreads,
mpfr::RoundingMode rounding);
virtual void check(T start, T stop, mpfr::RoundingMode rounding);
};

View File

@ -6,21 +6,46 @@
//
//===----------------------------------------------------------------------===//
#include "exhaustive_test.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/math/logf.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include "utils/UnitTest/FPMatcher.h"
#include "utils/UnitTest/Test.h"
using FPBits = __llvm_libc::fputil::FPBits<float>;
namespace mpfr = __llvm_libc::testing::mpfr;
TEST(LlvmLibcLogfExhaustiveTest, AllValues) {
uint32_t bits = 0U;
do {
FPBits xbits(bits);
float x = float(xbits);
EXPECT_MPFR_MATCH(mpfr::Operation::Log, x, __llvm_libc::logf(x), 0.5);
} while (bits++ < 0x7f7f'ffffU);
struct LlvmLibcLogfExhaustiveTest : public LlvmLibcExhaustiveTest<uint32_t> {
void check(uint32_t start, uint32_t stop,
mpfr::RoundingMode rounding) override {
mpfr::ForceRoundingMode r(rounding);
uint32_t bits = start;
do {
FPBits xbits(bits);
float x = float(xbits);
EXPECT_MPFR_MATCH(mpfr::Operation::Log, x, __llvm_libc::logf(x), 0.5,
rounding);
} while (bits++ < stop);
}
};
TEST_F(LlvmLibcLogfExhaustiveTest, RoundNearestTieToEven) {
test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
mpfr::RoundingMode::Nearest);
}
TEST_F(LlvmLibcLogfExhaustiveTest, RoundUp) {
test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
mpfr::RoundingMode::Upward);
}
TEST_F(LlvmLibcLogfExhaustiveTest, RoundDown) {
test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
mpfr::RoundingMode::Downward);
}
TEST_F(LlvmLibcLogfExhaustiveTest, RoundTowardZero) {
test_full_range(/*start=*/0U, /*stop=*/0x7f80'0000U, /*nthreads=*/16,
mpfr::RoundingMode::TowardZero);
}

View File

@ -16,6 +16,7 @@
#include <cmath>
#include <fenv.h>
#include <memory>
#include <sstream>
#include <stdint.h>
#include <string>
@ -571,6 +572,10 @@ ternary_operation_one_output(Operation op, InputType x, InputType y,
}
}
// Remark: For all the explain_*_error functions, we will use std::stringstream
// to build the complete error messages before sending it to the outstream `OS`
// once at the end. This will stop the error messages from interleaving when
// the tests are running concurrently.
template <typename T>
void explain_unary_operation_single_output_error(Operation op, T input,
T matchValue,
@ -582,18 +587,20 @@ void explain_unary_operation_single_output_error(Operation op, T input,
MPFRNumber mpfr_result;
mpfr_result = unary_operation(op, input, precision, rounding);
MPFRNumber mpfrMatchValue(matchValue);
OS << "Match value not within tolerance value of MPFR result:\n"
std::stringstream ss;
ss << "Match value not within tolerance value of MPFR result:\n"
<< " Input decimal: " << mpfrInput.str() << '\n';
__llvm_libc::fputil::testing::describeValue(" Input bits: ", input, OS);
OS << '\n' << " Match decimal: " << mpfrMatchValue.str() << '\n';
__llvm_libc::fputil::testing::describeValue(" Input bits: ", input, ss);
ss << '\n' << " Match decimal: " << mpfrMatchValue.str() << '\n';
__llvm_libc::fputil::testing::describeValue(" Match bits: ", matchValue,
OS);
OS << '\n' << " MPFR result: " << mpfr_result.str() << '\n';
ss);
ss << '\n' << " MPFR result: " << mpfr_result.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
" MPFR rounded: ", mpfr_result.as<T>(), OS);
OS << '\n';
OS << " ULP error: " << std::to_string(mpfr_result.ulp(matchValue))
" MPFR rounded: ", mpfr_result.as<T>(), ss);
ss << '\n';
ss << " ULP error: " << std::to_string(mpfr_result.ulp(matchValue))
<< '\n';
OS << ss.str();
}
template void
@ -616,31 +623,33 @@ void explain_unary_operation_two_outputs_error(
int mpfrIntResult;
MPFRNumber mpfr_result = unary_operation_two_outputs(op, input, mpfrIntResult,
precision, rounding);
std::stringstream ss;
if (mpfrIntResult != libc_result.i) {
OS << "MPFR integral result: " << mpfrIntResult << '\n'
ss << "MPFR integral result: " << mpfrIntResult << '\n'
<< "Libc integral result: " << libc_result.i << '\n';
} else {
OS << "Integral result from libc matches integral result from MPFR.\n";
ss << "Integral result from libc matches integral result from MPFR.\n";
}
MPFRNumber mpfrMatchValue(libc_result.f);
OS << "Libc floating point result is not within tolerance value of the MPFR "
ss << "Libc floating point result is not within tolerance value of the MPFR "
<< "result.\n\n";
OS << " Input decimal: " << mpfrInput.str() << "\n\n";
ss << " Input decimal: " << mpfrInput.str() << "\n\n";
OS << "Libc floating point value: " << mpfrMatchValue.str() << '\n';
ss << "Libc floating point value: " << mpfrMatchValue.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
" Libc floating point bits: ", libc_result.f, OS);
OS << "\n\n";
" Libc floating point bits: ", libc_result.f, ss);
ss << "\n\n";
OS << " MPFR result: " << mpfr_result.str() << '\n';
ss << " MPFR result: " << mpfr_result.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
" MPFR rounded: ", mpfr_result.as<T>(), OS);
OS << '\n'
" MPFR rounded: ", mpfr_result.as<T>(), ss);
ss << '\n'
<< " ULP error: "
<< std::to_string(mpfr_result.ulp(libc_result.f)) << '\n';
OS << ss.str();
}
template void explain_unary_operation_two_outputs_error<float>(
@ -665,17 +674,19 @@ void explain_binary_operation_two_outputs_error(
MPFRNumber mpfr_result = binary_operation_two_outputs(
op, input.x, input.y, mpfrIntResult, precision, rounding);
MPFRNumber mpfrMatchValue(libc_result.f);
std::stringstream ss;
OS << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n'
ss << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n'
<< "MPFR integral result: " << mpfrIntResult << '\n'
<< "Libc integral result: " << libc_result.i << '\n'
<< "Libc floating point result: " << mpfrMatchValue.str() << '\n'
<< " MPFR result: " << mpfr_result.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
"Libc floating point result bits: ", libc_result.f, OS);
"Libc floating point result bits: ", libc_result.f, ss);
__llvm_libc::fputil::testing::describeValue(
" MPFR rounded bits: ", mpfr_result.as<T>(), OS);
OS << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result.f)) << '\n';
" MPFR rounded bits: ", mpfr_result.as<T>(), ss);
ss << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result.f)) << '\n';
OS << ss.str();
}
template void explain_binary_operation_two_outputs_error<float>(
@ -701,20 +712,22 @@ void explain_binary_operation_one_output_error(
MPFRNumber mpfr_result =
binary_operation_one_output(op, input.x, input.y, precision, rounding);
MPFRNumber mpfrMatchValue(libc_result);
std::stringstream ss;
OS << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n';
ss << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n';
__llvm_libc::fputil::testing::describeValue("First input bits: ", input.x,
OS);
ss);
__llvm_libc::fputil::testing::describeValue("Second input bits: ", input.y,
OS);
ss);
OS << "Libc result: " << mpfrMatchValue.str() << '\n'
ss << "Libc result: " << mpfrMatchValue.str() << '\n'
<< "MPFR result: " << mpfr_result.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
"Libc floating point result bits: ", libc_result, OS);
"Libc floating point result bits: ", libc_result, ss);
__llvm_libc::fputil::testing::describeValue(
" MPFR rounded bits: ", mpfr_result.as<T>(), OS);
OS << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result)) << '\n';
" MPFR rounded bits: ", mpfr_result.as<T>(), ss);
ss << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result)) << '\n';
OS << ss.str();
}
template void explain_binary_operation_one_output_error<float>(
@ -741,23 +754,25 @@ void explain_ternary_operation_one_output_error(
MPFRNumber mpfr_result = ternary_operation_one_output(
op, input.x, input.y, input.z, precision, rounding);
MPFRNumber mpfrMatchValue(libc_result);
std::stringstream ss;
OS << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str()
ss << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str()
<< " z: " << mpfrZ.str() << '\n';
__llvm_libc::fputil::testing::describeValue("First input bits: ", input.x,
OS);
ss);
__llvm_libc::fputil::testing::describeValue("Second input bits: ", input.y,
OS);
ss);
__llvm_libc::fputil::testing::describeValue("Third input bits: ", input.z,
OS);
ss);
OS << "Libc result: " << mpfrMatchValue.str() << '\n'
ss << "Libc result: " << mpfrMatchValue.str() << '\n'
<< "MPFR result: " << mpfr_result.str() << '\n';
__llvm_libc::fputil::testing::describeValue(
"Libc floating point result bits: ", libc_result, OS);
"Libc floating point result bits: ", libc_result, ss);
__llvm_libc::fputil::testing::describeValue(
" MPFR rounded bits: ", mpfr_result.as<T>(), OS);
OS << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result)) << '\n';
" MPFR rounded bits: ", mpfr_result.as<T>(), ss);
ss << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result)) << '\n';
OS << ss.str();
}
template void explain_ternary_operation_one_output_error<float>(

View File

@ -10,6 +10,7 @@
#include "src/__support/FPUtil/FPBits.h"
#include <sstream>
#include <string>
namespace __llvm_libc {
@ -30,10 +31,9 @@ uintToHex(T X, size_t Length = sizeof(T) * 2) {
return s;
}
template <typename ValType>
template <typename ValType, typename StreamType>
cpp::EnableIfType<cpp::IsFloatingPointType<ValType>::Value, void>
describeValue(const char *label, ValType value,
testutils::StreamWrapper &stream) {
describeValue(const char *label, ValType value, StreamType &stream) {
stream << label;
FPBits<ValType> bits(value);
@ -49,15 +49,19 @@ describeValue(const char *label, ValType value,
(fputil::ExponentWidth<ValType>::VALUE - 1) / 4 + 1;
constexpr int mantissaWidthInHex =
(fputil::MantissaWidth<ValType>::VALUE - 1) / 4 + 1;
constexpr int bitsWidthInHex =
sizeof(typename fputil::FPBits<ValType>::UIntType) * 2;
stream << "Sign: " << (bits.get_sign() ? '1' : '0') << ", "
<< "Exponent: 0x"
stream << "0x"
<< uintToHex<typename fputil::FPBits<ValType>::UIntType>(
bits.uintval(), bitsWidthInHex)
<< ", (S | E | M) = (" << (bits.get_sign() ? '1' : '0') << " | 0x"
<< uintToHex<uint16_t>(bits.get_unbiased_exponent(),
exponentWidthInHex)
<< ", "
<< "Mantissa: 0x"
<< " | 0x"
<< uintToHex<typename fputil::FPBits<ValType>::UIntType>(
bits.get_mantissa(), mantissaWidthInHex);
bits.get_mantissa(), mantissaWidthInHex)
<< ")";
}
stream << '\n';
@ -70,6 +74,11 @@ template void describeValue<double>(const char *, double,
template void describeValue<long double>(const char *, long double,
testutils::StreamWrapper &);
template void describeValue<float>(const char *, float, std::stringstream &);
template void describeValue<double>(const char *, double, std::stringstream &);
template void describeValue<long double>(const char *, long double,
std::stringstream &);
} // namespace testing
} // namespace fputil
} // namespace __llvm_libc

View File

@ -17,10 +17,9 @@ namespace __llvm_libc {
namespace fputil {
namespace testing {
template <typename ValType>
template <typename ValType, typename StreamType>
cpp::EnableIfType<cpp::IsFloatingPointType<ValType>::Value, void>
describeValue(const char *label, ValType value,
testutils::StreamWrapper &stream);
describeValue(const char *label, ValType value, StreamType &stream);
template <typename T, __llvm_libc::testing::TestCondition Condition>
class FPMatcher : public __llvm_libc::testing::Matcher<T> {