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 
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 
33 static void Randomize(cpp::MutableArrayRef<char> buffer) {
34   for (auto &current : buffer)
35     current = GetRandomChar();
36 }
37 
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 
51   static BufferT splat(ubyte value) {
52     return bit_cast<BufferT>(Backend::template splat<T>(value));
53   }
54 
55   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 
59   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 
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 
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 
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 
131   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 
137   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 
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 
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