1 //===-- Unittests for backends --------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "src/__support/CPP/Array.h"
10 #include "src/__support/CPP/ArrayRef.h"
11 #include "src/__support/CPP/Bit.h"
12 #include "src/__support/architectures.h"
13 #include "src/string/memory_utils/backends.h"
14 #include "utils/UnitTest/Test.h"
15 #include <string.h>
16
17 namespace __llvm_libc {
18
19 template <size_t Size> using Buffer = cpp::Array<char, Size>;
20
GetRandomChar()21 static char GetRandomChar() {
22 // Implementation of C++ minstd_rand seeded with 123456789.
23 // https://en.cppreference.com/w/cpp/numeric/random
24 // "Minimum standard", recommended by Park, Miller, and Stockmeyer in 1993
25 static constexpr const uint64_t a = 48271;
26 static constexpr const uint64_t c = 0;
27 static constexpr const uint64_t m = 2147483647;
28 static uint64_t seed = 123456789;
29 seed = (a * seed + c) % m;
30 return seed;
31 }
32
Randomize(cpp::MutableArrayRef<char> buffer)33 static void Randomize(cpp::MutableArrayRef<char> buffer) {
34 for (auto ¤t : buffer)
35 current = GetRandomChar();
36 }
37
GetRandomBuffer()38 template <size_t Size> static Buffer<Size> GetRandomBuffer() {
39 Buffer<Size> buffer;
40 Randomize(buffer);
41 return buffer;
42 }
43
44 template <typename Backend, size_t Size> struct Conf {
45 static_assert(Backend::IS_BACKEND_TYPE);
46 using BufferT = Buffer<Size>;
47 using T = typename Backend::template getNextType<Size>;
48 static_assert(sizeof(T) == Size);
49 static constexpr size_t SIZE = Size;
50
splat__llvm_libc::Conf51 static BufferT splat(ubyte value) {
52 return bit_cast<BufferT>(Backend::template splat<T>(value));
53 }
54
notEquals__llvm_libc::Conf55 static uint64_t notEquals(const BufferT &v1, const BufferT &v2) {
56 return Backend::template notEquals<T>(bit_cast<T>(v1), bit_cast<T>(v2));
57 }
58
threeWayCmp__llvm_libc::Conf59 static int32_t threeWayCmp(const BufferT &v1, const BufferT &v2) {
60 return Backend::template threeWayCmp<T>(bit_cast<T>(v1), bit_cast<T>(v2));
61 }
62 };
63
64 using FunctionTypes = testing::TypeList< //
65 #if defined(LLVM_LIBC_ARCH_X86) //
66 Conf<X86Backend, 1>, //
67 Conf<X86Backend, 2>, //
68 Conf<X86Backend, 4>, //
69 Conf<X86Backend, 8>, //
70 #if HAS_M128
71 Conf<X86Backend, 16>, //
72 #endif
73 #if HAS_M256
74 Conf<X86Backend, 32>, //
75 #endif
76 #if HAS_M512
77 Conf<X86Backend, 64>, //
78 #endif
79 #endif // defined(LLVM_LIBC_ARCH_X86)
80 Conf<Scalar64BitBackend, 1>, //
81 Conf<Scalar64BitBackend, 2>, //
82 Conf<Scalar64BitBackend, 4>, //
83 Conf<Scalar64BitBackend, 8> //
84 >;
85
TYPED_TEST(LlvmLibcMemoryBackend,splat,FunctionTypes)86 TYPED_TEST(LlvmLibcMemoryBackend, splat, FunctionTypes) {
87 for (auto value : cpp::Array<uint8_t, 3>{0u, 1u, 255u}) {
88 alignas(64) const auto stored = ParamType::splat(bit_cast<ubyte>(value));
89 for (size_t i = 0; i < ParamType::SIZE; ++i)
90 EXPECT_EQ(bit_cast<uint8_t>(stored[i]), value);
91 }
92 }
93
TYPED_TEST(LlvmLibcMemoryBackend,notEquals,FunctionTypes)94 TYPED_TEST(LlvmLibcMemoryBackend, notEquals, FunctionTypes) {
95 alignas(64) const auto a = GetRandomBuffer<ParamType::SIZE>();
96 EXPECT_EQ(ParamType::notEquals(a, a), uint64_t(0));
97 for (size_t i = 0; i < a.size(); ++i) {
98 alignas(64) auto b = a;
99 ++b[i];
100 EXPECT_NE(ParamType::notEquals(a, b), uint64_t(0));
101 EXPECT_NE(ParamType::notEquals(b, a), uint64_t(0));
102 }
103 }
104
TYPED_TEST(LlvmLibcMemoryBackend,threeWayCmp,FunctionTypes)105 TYPED_TEST(LlvmLibcMemoryBackend, threeWayCmp, FunctionTypes) {
106 alignas(64) const auto a = GetRandomBuffer<ParamType::SIZE>();
107 EXPECT_EQ(ParamType::threeWayCmp(a, a), 0);
108 for (size_t i = 0; i < a.size(); ++i) {
109 alignas(64) auto b = a;
110 ++b[i];
111 const auto cmp = memcmp(&a, &b, sizeof(a));
112 ASSERT_NE(cmp, 0);
113 if (cmp > 0) {
114 EXPECT_GT(ParamType::threeWayCmp(a, b), 0);
115 EXPECT_LT(ParamType::threeWayCmp(b, a), 0);
116 } else {
117 EXPECT_LT(ParamType::threeWayCmp(a, b), 0);
118 EXPECT_GT(ParamType::threeWayCmp(b, a), 0);
119 }
120 }
121 }
122
123 template <typename Backend, size_t Size, Temporality TS, Aligned AS>
124 struct LoadStoreConf {
125 static_assert(Backend::IS_BACKEND_TYPE);
126 using BufferT = Buffer<Size>;
127 using T = typename Backend::template getNextType<Size>;
128 static_assert(sizeof(T) == Size);
129 static constexpr size_t SIZE = Size;
130
load__llvm_libc::LoadStoreConf131 static BufferT load(const BufferT &ref) {
132 const auto *ptr = bit_cast<const T *>(ref.data());
133 const T value = Backend::template load<T, TS, AS>(ptr);
134 return bit_cast<BufferT>(value);
135 }
136
store__llvm_libc::LoadStoreConf137 static void store(BufferT &ref, const BufferT value) {
138 auto *ptr = bit_cast<T *>(ref.data());
139 Backend::template store<T, TS, AS>(ptr, bit_cast<T>(value));
140 }
141 };
142
143 using LoadStoreTypes = testing::TypeList< //
144 #if defined(LLVM_LIBC_ARCH_X86) //
145 LoadStoreConf<X86Backend, 1, Temporality::TEMPORAL, Aligned::NO>, //
146 LoadStoreConf<X86Backend, 1, Temporality::TEMPORAL, Aligned::YES>, //
147 LoadStoreConf<X86Backend, 2, Temporality::TEMPORAL, Aligned::NO>, //
148 LoadStoreConf<X86Backend, 2, Temporality::TEMPORAL, Aligned::YES>, //
149 LoadStoreConf<X86Backend, 4, Temporality::TEMPORAL, Aligned::NO>, //
150 LoadStoreConf<X86Backend, 4, Temporality::TEMPORAL, Aligned::YES>, //
151 LoadStoreConf<X86Backend, 8, Temporality::TEMPORAL, Aligned::NO>, //
152 LoadStoreConf<X86Backend, 8, Temporality::TEMPORAL, Aligned::YES>, //
153 #if HAS_M128
154 LoadStoreConf<X86Backend, 16, Temporality::TEMPORAL, Aligned::NO>, //
155 LoadStoreConf<X86Backend, 16, Temporality::TEMPORAL, Aligned::YES>, //
156 LoadStoreConf<X86Backend, 16, Temporality::NON_TEMPORAL, Aligned::YES>, //
157 #endif
158 #if HAS_M256
159 LoadStoreConf<X86Backend, 32, Temporality::TEMPORAL, Aligned::NO>, //
160 LoadStoreConf<X86Backend, 32, Temporality::TEMPORAL, Aligned::YES>, //
161 LoadStoreConf<X86Backend, 32, Temporality::NON_TEMPORAL, Aligned::YES>, //
162 #endif
163 #if HAS_M512
164 LoadStoreConf<X86Backend, 64, Temporality::TEMPORAL, Aligned::NO>, //
165 LoadStoreConf<X86Backend, 64, Temporality::TEMPORAL, Aligned::YES>, //
166 LoadStoreConf<X86Backend, 64, Temporality::NON_TEMPORAL, Aligned::YES>, //
167 #endif
168 #endif // defined(LLVM_LIBC_ARCH_X86)
169 LoadStoreConf<Scalar64BitBackend, 1, Temporality::TEMPORAL, Aligned::NO>, //
170 LoadStoreConf<Scalar64BitBackend, 1, Temporality::TEMPORAL,
171 Aligned::YES>, //
172 LoadStoreConf<Scalar64BitBackend, 2, Temporality::TEMPORAL, Aligned::NO>, //
173 LoadStoreConf<Scalar64BitBackend, 2, Temporality::TEMPORAL,
174 Aligned::YES>, //
175 LoadStoreConf<Scalar64BitBackend, 4, Temporality::TEMPORAL, Aligned::NO>, //
176 LoadStoreConf<Scalar64BitBackend, 4, Temporality::TEMPORAL,
177 Aligned::YES>, //
178 LoadStoreConf<Scalar64BitBackend, 8, Temporality::TEMPORAL, Aligned::NO>, //
179 LoadStoreConf<Scalar64BitBackend, 8, Temporality::TEMPORAL, Aligned::YES> //
180 >;
181
TYPED_TEST(LlvmLibcMemoryBackend,load,LoadStoreTypes)182 TYPED_TEST(LlvmLibcMemoryBackend, load, LoadStoreTypes) {
183 alignas(64) const auto expected = GetRandomBuffer<ParamType::SIZE>();
184 const auto loaded = ParamType::load(expected);
185 for (size_t i = 0; i < ParamType::SIZE; ++i)
186 EXPECT_EQ(loaded[i], expected[i]);
187 }
188
TYPED_TEST(LlvmLibcMemoryBackend,store,LoadStoreTypes)189 TYPED_TEST(LlvmLibcMemoryBackend, store, LoadStoreTypes) {
190 alignas(64) const auto expected = GetRandomBuffer<ParamType::SIZE>();
191 alignas(64) typename ParamType::BufferT stored;
192 ParamType::store(stored, expected);
193 for (size_t i = 0; i < ParamType::SIZE; ++i)
194 EXPECT_EQ(stored[i], expected[i]);
195 }
196
197 } // namespace __llvm_libc
198