1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
#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;
}
|