File: fuzzer_simd.cpp

package info (click to toggle)
libdivide 5.2.0-0.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 17,564 kB
  • sloc: ansic: 131,647; cpp: 70,136; python: 47; makefile: 3
file content (149 lines) | stat: -rw-r--r-- 5,180 bytes parent folder | download
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;
}