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 ¤t : 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