| #include <array> |
| #include <cstdint> |
| #include <cstring> |
| #include <limits> |
| |
| #include "libdivide.h" |
| |
| #if __cplusplus < 201703L |
| #error "Sorry, needs C++17 or later." |
| #endif |
| |
| // How many bytes of data to use for numerators at most. |
| // Must be larger than the largest simd size. |
| constexpr const std::size_t NbytesOfInput = 512 / 8; |
| |
| // how much data to consume for the divisor (at most). |
| constexpr const std::size_t NbytesForDivisor = sizeof(std::int64_t); |
| |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { |
| // pick what types to operate on from the fuzz data |
| if (Size < 2) return 0; |
| const auto type_selector = Data[0]; |
| const auto branchfree_selector = Data[1]; |
| Data += 2; |
| Size -= 2; |
| |
| // exit early to avoid reading outside bounds later |
| if (Size < NbytesOfInput + NbytesForDivisor) { |
| return 0; |
| } |
| |
| // This generic lambda does the work for us, by conveniently deducing the type |
| // It will return an int, just so the switch case looks prettier at the |
| // call site. |
| auto outer = [&](const auto integerdummy) -> int { |
| auto inner = [&](const auto branchingtypedummy) -> int { |
| using Integer = std::remove_const_t<decltype(integerdummy)>; |
| static_assert(std::is_integral_v<Integer>, "input should be an integer"); |
| |
| // pick the divisor from the fuzz data |
| const Integer divisor = [&]() { |
| static_assert(sizeof(Integer) <= NbytesForDivisor, "make NbytesForDivisor larger"); |
| Integer tmp; |
| std::memcpy(&tmp, Data, sizeof(Integer)); |
| Data += NbytesForDivisor; |
| Size -= NbytesForDivisor; |
| return tmp; |
| }(); |
| |
| // don't let the universe explode |
| if (divisor == 0) { |
| return 0; |
| } |
| |
| // respect the branchfree variant prohibiting 1 |
| if (branchingtypedummy.value == libdivide::BRANCHFREE && divisor == 1) { |
| return 0; |
| } |
| |
| // This array type is used for both input and output. It may be overly |
| // large for the smaller simd types. |
| using ArrayOfIntegers = std::array<Integer, NbytesOfInput / sizeof(Integer)>; |
| |
| // The numbers to later divide by divisor |
| ArrayOfIntegers numerators; |
| numerators.fill(0); |
| if (Size < NbytesOfInput) return 0; |
| std::memcpy(numerators.data(), Data, NbytesOfInput); |
| |
| // avoid problems with integer overflow from INT_MIN/-1 |
| if (std::is_signed_v<Integer> && divisor == -1) { |
| for (auto& e : numerators) { |
| if (e == std::numeric_limits<Integer>::min()) { |
| e = 0; |
| } |
| } |
| } |
| |
| // get data into a simd register |
| #if defined(LIBDIVIDE_AVX512) |
| // not tested! |
| using Vector = __m512i; |
| const Vector num_as_simdvector = _mm512_loadu_si512((const Vector*)numerators.data()); |
| #endif |
| |
| #if defined(LIBDIVIDE_AVX2) |
| using Vector = __m256i; |
| const Vector num_as_simdvector = _mm256_loadu_si256((const Vector*)numerators.data()); |
| #endif |
| |
| #if defined(LIBDIVIDE_SSE2) |
| using Vector = __m128i; |
| const Vector num_as_simdvector = _mm_loadu_si128((const Vector*)numerators.data()); |
| #endif |
| // carry out the division |
| libdivide::divider<Integer, branchingtypedummy.value> divider(divisor); |
| const Vector res = num_as_simdvector / divider; |
| |
| // this will eventually contain the result |
| ArrayOfIntegers simdresult; |
| simdresult.fill(0); |
| |
| // copy the results from the simd register |
| #if defined(LIBDIVIDE_AVX512) |
| _mm512_storeu_si512((Vector*)simdresult.data(), res); |
| #endif |
| |
| #if defined(LIBDIVIDE_AVX2) |
| _mm256_storeu_si256((Vector*)simdresult.data(), res); |
| #endif |
| |
| #if defined(LIBDIVIDE_SSE2) |
| _mm_storeu_si128((Vector*)simdresult.data(), res); |
| #endif |
| |
| // how many elements will be assigned? |
| constexpr const std::size_t Nelements = sizeof(Vector) / sizeof(Integer); |
| |
| // validate the result |
| for (std::size_t i = 0; i < Nelements; ++i) { |
| const Integer expected = numerators.at(i) / divisor; |
| const Integer actual = simdresult.at(i); |
| if (expected != actual) abort(); |
| } |
| return 0; |
| }; |
| |
| if (branchfree_selector == libdivide::BRANCHFULL) { |
| return inner(std::integral_constant<int, libdivide::BRANCHFULL>{}); |
| } else if (branchfree_selector == libdivide::BRANCHFREE) { |
| return inner(std::integral_constant<int, libdivide::BRANCHFREE>{}); |
| } |
| return 0; |
| }; |
| |
| switch (type_selector) { |
| case 0: |
| return outer(std::uint32_t{}); |
| case 1: |
| return outer(std::int32_t{}); |
| case 2: |
| return outer(std::uint64_t{}); |
| case 3: |
| return outer(std::int64_t{}); |
| default: |
| return 0; |
| } |
| return 0; |
| } |