| //===-- Unittests for backends --------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "src/__support/CPP/ArrayRef.h" |
| #include "src/__support/CPP/Bit.h" |
| #include "src/__support/CPP/array.h" |
| #include "src/__support/architectures.h" |
| #include "src/string/memory_utils/backends.h" |
| #include "utils/UnitTest/Test.h" |
| #include <string.h> |
| |
| namespace __llvm_libc { |
| |
| template <size_t Size> using Buffer = cpp::array<char, Size>; |
| |
| static char GetRandomChar() { |
| // Implementation of C++ minstd_rand seeded with 123456789. |
| // https://en.cppreference.com/w/cpp/numeric/random |
| // "Minimum standard", recommended by Park, Miller, and Stockmeyer in 1993 |
| static constexpr const uint64_t a = 48271; |
| static constexpr const uint64_t c = 0; |
| static constexpr const uint64_t m = 2147483647; |
| static uint64_t seed = 123456789; |
| seed = (a * seed + c) % m; |
| return seed; |
| } |
| |
| static void Randomize(cpp::MutableArrayRef<char> buffer) { |
| for (auto ¤t : buffer) |
| current = GetRandomChar(); |
| } |
| |
| template <size_t Size> static Buffer<Size> GetRandomBuffer() { |
| Buffer<Size> buffer; |
| Randomize(buffer); |
| return buffer; |
| } |
| |
| template <typename Backend, size_t Size> struct Conf { |
| static_assert(Backend::IS_BACKEND_TYPE); |
| using BufferT = Buffer<Size>; |
| using T = typename Backend::template getNextType<Size>; |
| static_assert(sizeof(T) == Size); |
| static constexpr size_t SIZE = Size; |
| |
| static BufferT splat(ubyte value) { |
| return bit_cast<BufferT>(Backend::template splat<T>(value)); |
| } |
| |
| static uint64_t notEquals(const BufferT &v1, const BufferT &v2) { |
| return Backend::template notEquals<T>(bit_cast<T>(v1), bit_cast<T>(v2)); |
| } |
| |
| static int32_t threeWayCmp(const BufferT &v1, const BufferT &v2) { |
| return Backend::template threeWayCmp<T>(bit_cast<T>(v1), bit_cast<T>(v2)); |
| } |
| }; |
| |
| using FunctionTypes = testing::TypeList< // |
| #if defined(LLVM_LIBC_ARCH_X86) // |
| Conf<X86Backend, 1>, // |
| Conf<X86Backend, 2>, // |
| Conf<X86Backend, 4>, // |
| Conf<X86Backend, 8>, // |
| #if HAS_M128 |
| Conf<X86Backend, 16>, // |
| #endif |
| #if HAS_M256 |
| Conf<X86Backend, 32>, // |
| #endif |
| #if HAS_M512 |
| Conf<X86Backend, 64>, // |
| #endif |
| #endif // defined(LLVM_LIBC_ARCH_X86) |
| Conf<Scalar64BitBackend, 1>, // |
| Conf<Scalar64BitBackend, 2>, // |
| Conf<Scalar64BitBackend, 4>, // |
| Conf<Scalar64BitBackend, 8> // |
| >; |
| |
| TYPED_TEST(LlvmLibcMemoryBackend, splat, FunctionTypes) { |
| for (auto value : cpp::array<uint8_t, 3>{0u, 1u, 255u}) { |
| alignas(64) const auto stored = ParamType::splat(bit_cast<ubyte>(value)); |
| for (size_t i = 0; i < ParamType::SIZE; ++i) |
| EXPECT_EQ(bit_cast<uint8_t>(stored[i]), value); |
| } |
| } |
| |
| TYPED_TEST(LlvmLibcMemoryBackend, notEquals, FunctionTypes) { |
| alignas(64) const auto a = GetRandomBuffer<ParamType::SIZE>(); |
| EXPECT_EQ(ParamType::notEquals(a, a), uint64_t(0)); |
| for (size_t i = 0; i < a.size(); ++i) { |
| alignas(64) auto b = a; |
| ++b[i]; |
| EXPECT_NE(ParamType::notEquals(a, b), uint64_t(0)); |
| EXPECT_NE(ParamType::notEquals(b, a), uint64_t(0)); |
| } |
| } |
| |
| TYPED_TEST(LlvmLibcMemoryBackend, threeWayCmp, FunctionTypes) { |
| alignas(64) const auto a = GetRandomBuffer<ParamType::SIZE>(); |
| EXPECT_EQ(ParamType::threeWayCmp(a, a), 0); |
| for (size_t i = 0; i < a.size(); ++i) { |
| alignas(64) auto b = a; |
| ++b[i]; |
| const auto cmp = memcmp(&a, &b, sizeof(a)); |
| ASSERT_NE(cmp, 0); |
| if (cmp > 0) { |
| EXPECT_GT(ParamType::threeWayCmp(a, b), 0); |
| EXPECT_LT(ParamType::threeWayCmp(b, a), 0); |
| } else { |
| EXPECT_LT(ParamType::threeWayCmp(a, b), 0); |
| EXPECT_GT(ParamType::threeWayCmp(b, a), 0); |
| } |
| } |
| } |
| |
| template <typename Backend, size_t Size, Temporality TS, Aligned AS> |
| struct LoadStoreConf { |
| static_assert(Backend::IS_BACKEND_TYPE); |
| using BufferT = Buffer<Size>; |
| using T = typename Backend::template getNextType<Size>; |
| static_assert(sizeof(T) == Size); |
| static constexpr size_t SIZE = Size; |
| |
| static BufferT load(const BufferT &ref) { |
| const auto *ptr = bit_cast<const T *>(ref.data()); |
| const T value = Backend::template load<T, TS, AS>(ptr); |
| return bit_cast<BufferT>(value); |
| } |
| |
| static void store(BufferT &ref, const BufferT value) { |
| auto *ptr = bit_cast<T *>(ref.data()); |
| Backend::template store<T, TS, AS>(ptr, bit_cast<T>(value)); |
| } |
| }; |
| |
| using LoadStoreTypes = testing::TypeList< // |
| #if defined(LLVM_LIBC_ARCH_X86) // |
| LoadStoreConf<X86Backend, 1, Temporality::TEMPORAL, Aligned::NO>, // |
| LoadStoreConf<X86Backend, 1, Temporality::TEMPORAL, Aligned::YES>, // |
| LoadStoreConf<X86Backend, 2, Temporality::TEMPORAL, Aligned::NO>, // |
| LoadStoreConf<X86Backend, 2, Temporality::TEMPORAL, Aligned::YES>, // |
| LoadStoreConf<X86Backend, 4, Temporality::TEMPORAL, Aligned::NO>, // |
| LoadStoreConf<X86Backend, 4, Temporality::TEMPORAL, Aligned::YES>, // |
| LoadStoreConf<X86Backend, 8, Temporality::TEMPORAL, Aligned::NO>, // |
| LoadStoreConf<X86Backend, 8, Temporality::TEMPORAL, Aligned::YES>, // |
| #if HAS_M128 |
| LoadStoreConf<X86Backend, 16, Temporality::TEMPORAL, Aligned::NO>, // |
| LoadStoreConf<X86Backend, 16, Temporality::TEMPORAL, Aligned::YES>, // |
| LoadStoreConf<X86Backend, 16, Temporality::NON_TEMPORAL, Aligned::YES>, // |
| #endif |
| #if HAS_M256 |
| LoadStoreConf<X86Backend, 32, Temporality::TEMPORAL, Aligned::NO>, // |
| LoadStoreConf<X86Backend, 32, Temporality::TEMPORAL, Aligned::YES>, // |
| LoadStoreConf<X86Backend, 32, Temporality::NON_TEMPORAL, Aligned::YES>, // |
| #endif |
| #if HAS_M512 |
| LoadStoreConf<X86Backend, 64, Temporality::TEMPORAL, Aligned::NO>, // |
| LoadStoreConf<X86Backend, 64, Temporality::TEMPORAL, Aligned::YES>, // |
| LoadStoreConf<X86Backend, 64, Temporality::NON_TEMPORAL, Aligned::YES>, // |
| #endif |
| #endif // defined(LLVM_LIBC_ARCH_X86) |
| LoadStoreConf<Scalar64BitBackend, 1, Temporality::TEMPORAL, Aligned::NO>, // |
| LoadStoreConf<Scalar64BitBackend, 1, Temporality::TEMPORAL, |
| Aligned::YES>, // |
| LoadStoreConf<Scalar64BitBackend, 2, Temporality::TEMPORAL, Aligned::NO>, // |
| LoadStoreConf<Scalar64BitBackend, 2, Temporality::TEMPORAL, |
| Aligned::YES>, // |
| LoadStoreConf<Scalar64BitBackend, 4, Temporality::TEMPORAL, Aligned::NO>, // |
| LoadStoreConf<Scalar64BitBackend, 4, Temporality::TEMPORAL, |
| Aligned::YES>, // |
| LoadStoreConf<Scalar64BitBackend, 8, Temporality::TEMPORAL, Aligned::NO>, // |
| LoadStoreConf<Scalar64BitBackend, 8, Temporality::TEMPORAL, Aligned::YES> // |
| >; |
| |
| TYPED_TEST(LlvmLibcMemoryBackend, load, LoadStoreTypes) { |
| alignas(64) const auto expected = GetRandomBuffer<ParamType::SIZE>(); |
| const auto loaded = ParamType::load(expected); |
| for (size_t i = 0; i < ParamType::SIZE; ++i) |
| EXPECT_EQ(loaded[i], expected[i]); |
| } |
| |
| TYPED_TEST(LlvmLibcMemoryBackend, store, LoadStoreTypes) { |
| alignas(64) const auto expected = GetRandomBuffer<ParamType::SIZE>(); |
| alignas(64) typename ParamType::BufferT stored; |
| ParamType::store(stored, expected); |
| for (size_t i = 0; i < ParamType::SIZE; ++i) |
| EXPECT_EQ(stored[i], expected[i]); |
| } |
| |
| } // namespace __llvm_libc |