diff --git a/libc/test/src/string/bcmp_test.cpp b/libc/test/src/string/bcmp_test.cpp --- a/libc/test/src/string/bcmp_test.cpp +++ b/libc/test/src/string/bcmp_test.cpp @@ -6,9 +6,12 @@ // //===----------------------------------------------------------------------===// +#include "memory_utils/memory_check_utils.h" #include "src/string/bcmp.h" #include "utils/UnitTest/Test.h" +namespace __llvm_libc { + TEST(LlvmLibcBcmpTest, CmpZeroByte) { const char *lhs = "ab"; const char *rhs = "bc"; @@ -33,26 +36,22 @@ ASSERT_NE(__llvm_libc::bcmp(lhs, rhs, 2), 0); } -TEST(LlvmLibcBcmpTest, Sweep) { - static constexpr size_t K_MAX_SIZE = 1024; - char lhs[K_MAX_SIZE]; - char rhs[K_MAX_SIZE]; - - const auto reset = [](char *const ptr) { - for (size_t i = 0; i < K_MAX_SIZE; ++i) - ptr[i] = 'a'; - }; - - reset(lhs); - reset(rhs); - for (size_t i = 0; i < K_MAX_SIZE; ++i) - ASSERT_EQ(__llvm_libc::bcmp(lhs, rhs, i), 0); - - reset(lhs); - reset(rhs); - for (size_t i = 0; i < K_MAX_SIZE; ++i) { - rhs[i] = 'b'; - ASSERT_NE(__llvm_libc::bcmp(lhs, rhs, K_MAX_SIZE), 0); - rhs[i] = 'a'; +template +int SpanCmpAdaptor(cpp::span p1, cpp::span p2, size_t size) { + return FnImpl(p1.begin(), p2.begin(), size); +} + +TEST(LlvmLibcBcmpTest, Thorough) { + static constexpr size_t kSize = 1024; + static constexpr auto Impl = SpanCmpAdaptor<__llvm_libc::bcmp>; + Buffer Buffer1(kSize); + Buffer Buffer2(kSize); + Randomize(Buffer1.span()); + for (size_t size = 0; size < kSize; ++size) { + auto span1 = Buffer1.span().subspan(0, size); + auto span2 = Buffer2.span().subspan(0, size); + ASSERT_TRUE((CheckBcmp(span1, span2, size))); } } + +} // namespace __llvm_libc diff --git a/libc/test/src/string/bzero_test.cpp b/libc/test/src/string/bzero_test.cpp --- a/libc/test/src/string/bzero_test.cpp +++ b/libc/test/src/string/bzero_test.cpp @@ -6,44 +6,26 @@ // //===----------------------------------------------------------------------===// -#include "src/__support/CPP/span.h" +#include "memory_utils/memory_check_utils.h" #include "src/string/bzero.h" #include "utils/UnitTest/Test.h" -using __llvm_libc::cpp::array; -using __llvm_libc::cpp::span; -using Data = array; +namespace __llvm_libc { -static const span k_deadcode("DEADC0DE", 8); - -// Returns a Data object filled with a repetition of `filler`. -Data get_data(span filler) { - Data out; - for (size_t i = 0; i < out.size(); ++i) - out[i] = filler[i % filler.size()]; - return out; +template +void SpanZeroAdaptor(cpp::span p1, uint8_t value, size_t size) { + assert(value == 0); + FnImpl(p1.begin(), size); } TEST(LlvmLibcBzeroTest, Thorough) { - const Data dirty = get_data(k_deadcode); - for (size_t count = 0; count < 1024; ++count) { - for (size_t align = 0; align < 64; ++align) { - auto buffer = dirty; - char *const dst = &buffer[align]; - __llvm_libc::bzero(dst, count); - // Everything before copy is untouched. - for (size_t i = 0; i < align; ++i) - ASSERT_EQ(buffer[i], dirty[i]); - // Everything in between is copied. - for (size_t i = 0; i < count; ++i) - ASSERT_EQ(buffer[align + i], char(0)); - // Everything after copy is untouched. - for (size_t i = align + count; i < dirty.size(); ++i) - ASSERT_EQ(buffer[i], dirty[i]); - } + static constexpr size_t kSize = 1024; + static constexpr auto Impl = SpanZeroAdaptor<__llvm_libc::bzero>; + Buffer DstBuffer(kSize); + for (size_t size = 0; size < kSize; ++size) { + auto dst = DstBuffer.span().subspan(0, size); + ASSERT_TRUE((CheckMemset(dst, 0, size))); } } -// FIXME: Add tests with reads and writes on the boundary of a read/write -// protected page to check we're not reading nor writing prior/past the allowed -// regions. +} // namespace __llvm_libc diff --git a/libc/test/src/string/memcmp_test.cpp b/libc/test/src/string/memcmp_test.cpp --- a/libc/test/src/string/memcmp_test.cpp +++ b/libc/test/src/string/memcmp_test.cpp @@ -6,9 +6,12 @@ // //===----------------------------------------------------------------------===// +#include "memory_utils/memory_check_utils.h" #include "src/string/memcmp.h" #include "utils/UnitTest/Test.h" +namespace __llvm_libc { + TEST(LlvmLibcMemcmpTest, CmpZeroByte) { const char *lhs = "ab"; const char *rhs = "yz"; @@ -33,26 +36,22 @@ EXPECT_GT(__llvm_libc::memcmp(lhs, rhs, 2), 0); } -TEST(LlvmLibcMemcmpTest, Sweep) { - static constexpr size_t K_MAX_SIZE = 1024; - char lhs[K_MAX_SIZE]; - char rhs[K_MAX_SIZE]; - - const auto reset = [](char *const ptr) { - for (size_t i = 0; i < K_MAX_SIZE; ++i) - ptr[i] = 'a'; - }; - - reset(lhs); - reset(rhs); - for (size_t i = 0; i < K_MAX_SIZE; ++i) - ASSERT_EQ(__llvm_libc::memcmp(lhs, rhs, i), 0); - - reset(lhs); - reset(rhs); - for (size_t i = 0; i < K_MAX_SIZE; ++i) { - rhs[i] = 'z'; - ASSERT_LT(__llvm_libc::memcmp(lhs, rhs, K_MAX_SIZE), 0); - rhs[i] = 'a'; +template +int SpanCmpAdaptor(cpp::span p1, cpp::span p2, size_t size) { + return FnImpl(p1.begin(), p2.begin(), size); +} + +TEST(LlvmLibcMemcmpTest, Thorough) { + static constexpr size_t kSize = 1024; + static constexpr auto Impl = SpanCmpAdaptor<__llvm_libc::memcmp>; + Buffer Buffer1(kSize); + Buffer Buffer2(kSize); + Randomize(Buffer1.span()); + for (size_t size = 0; size < kSize; ++size) { + auto span1 = Buffer1.span().subspan(0, size); + auto span2 = Buffer2.span().subspan(0, size); + ASSERT_TRUE((CheckMemcmp(span1, span2, size))); } } + +} // namespace __llvm_libc diff --git a/libc/test/src/string/memcpy_test.cpp b/libc/test/src/string/memcpy_test.cpp --- a/libc/test/src/string/memcpy_test.cpp +++ b/libc/test/src/string/memcpy_test.cpp @@ -6,49 +6,28 @@ // //===----------------------------------------------------------------------===// -#include "src/__support/CPP/span.h" +#include "memory_utils/memory_check_utils.h" #include "src/string/memcpy.h" #include "utils/UnitTest/Test.h" -using __llvm_libc::cpp::array; -using __llvm_libc::cpp::span; -using Data = array; +namespace __llvm_libc { -static const span k_numbers("0123456789", 10); -static const span k_deadcode("DEADC0DE", 8); - -// Returns a Data object filled with a repetition of `filler`. -Data get_data(span filler) { - Data out; - for (size_t i = 0; i < out.size(); ++i) - out[i] = filler[i % filler.size()]; - return out; +template +void SpanCopyAdaptor(cpp::span dst, cpp::span src, size_t size) { + FnImpl(dst.begin(), src.begin(), size); } TEST(LlvmLibcMemcpyTest, Thorough) { - const Data groundtruth = get_data(k_numbers); - const Data dirty = get_data(k_deadcode); - for (size_t count = 0; count < 1024; ++count) { - for (size_t align = 0; align < 64; ++align) { - auto buffer = dirty; - const char *const src = groundtruth.data(); - void *const dst = &buffer[align]; - void *const ret = __llvm_libc::memcpy(dst, src, count); - // Return value is `dst`. - ASSERT_EQ(ret, dst); - // Everything before copy is untouched. - for (size_t i = 0; i < align; ++i) - ASSERT_EQ(buffer[i], dirty[i]); - // Everything in between is copied. - for (size_t i = 0; i < count; ++i) - ASSERT_EQ(buffer[align + i], groundtruth[i]); - // Everything after copy is untouched. - for (size_t i = align + count; i < dirty.size(); ++i) - ASSERT_EQ(buffer[i], dirty[i]); - } + static constexpr size_t kSize = 1024; + static constexpr auto Impl = SpanCopyAdaptor<__llvm_libc::memcpy>; + Buffer SrcBuffer(kSize); + Buffer DstBuffer(kSize); + Randomize(SrcBuffer.span()); + for (size_t size = 0; size < kSize; ++size) { + auto src = SrcBuffer.span().subspan(0, size); + auto dst = DstBuffer.span().subspan(0, size); + ASSERT_TRUE(CheckMemcpy(dst, src, size)); } } -// FIXME: Add tests with reads and writes on the boundary of a read/write -// protected page to check we're not reading nor writing prior/past the allowed -// regions. +} // namespace __llvm_libc diff --git a/libc/test/src/string/memory_utils/memory_check_utils.h b/libc/test/src/string/memory_utils/memory_check_utils.h new file mode 100644 --- /dev/null +++ b/libc/test/src/string/memory_utils/memory_check_utils.h @@ -0,0 +1,160 @@ +//===-- Utils to test conformance of mem functions ------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LIBC_TEST_SRC_STRING_MEMORY_UTILS_MEMORY_CHECK_UTILS_H +#define LIBC_TEST_SRC_STRING_MEMORY_UTILS_MEMORY_CHECK_UTILS_H + +#include "src/__support/CPP/span.h" +#include "src/string/memory_utils/utils.h" +#include // assert +#include // size_t +#include // uintxx_t +#include // malloc/free + +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +#include +#define ASAN_POISON_MEMORY_REGION(addr, size) \ + __asan_poison_memory_region((addr), (size)) +#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ + __asan_unpoison_memory_region((addr), (size)) +#else +#define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) +#define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) +#endif + +namespace __llvm_libc { +// Simple structure to allocate a buffer of a particular size. +struct PoisonedBuffer { + PoisonedBuffer(size_t size) : ptr((char *)malloc(size)) { + assert(ptr); + ASAN_POISON_MEMORY_REGION(ptr, size); + } + ~PoisonedBuffer() { free(ptr); } + +protected: + char *ptr = nullptr; +}; + +// Simple structure to allocate a buffer (aligned or not) of a particular size. +// It is backed by a wider buffer that is marked poisoned when ASAN is present. +// The requested region is unpoisoned, this allows catching out of bounds +// accesses. +enum class Aligned : bool { NO = false, YES = true }; +struct Buffer : private PoisonedBuffer { + static constexpr size_t kAlign = 64; + static constexpr size_t kLeeway = 2 * kAlign; + Buffer(size_t size, Aligned aligned = Aligned::YES) + : PoisonedBuffer(size + kLeeway), size(size) { + offset_ptr = ptr; + offset_ptr += distance_to_next_aligned(ptr); + assert((uintptr_t)(offset_ptr) % kAlign == 0); + if (aligned == Aligned::NO) + ++offset_ptr; + assert(offset_ptr > ptr); + assert((offset_ptr + size) < (ptr + size + kLeeway)); + ASAN_UNPOISON_MEMORY_REGION(offset_ptr, size); + } + cpp::span span() { return cpp::span(offset_ptr, size); } + +private: + size_t size = 0; + char *offset_ptr = nullptr; +}; + +static inline char GetRandomChar() { + static constexpr const uint64_t a = 1103515245; + static constexpr const uint64_t c = 12345; + static constexpr const uint64_t m = 1ULL << 31; + static uint64_t seed = 123456789; + seed = (a * seed + c) % m; + return seed; +} + +// Randomize the content of the buffer. +static inline void Randomize(cpp::span buffer) { + for (auto ¤t : buffer) + current = GetRandomChar(); +} + +// Copy one span to another. +static inline void Copy(cpp::span dst, const cpp::span src) { + assert(dst.size() == src.size()); + for (size_t i = 0; i < dst.size(); ++i) + dst[i] = src[i]; +} + +template +bool CheckMemcpy(cpp::span dst, cpp::span src, size_t size) { + assert(dst.size() == src.size()); + assert(dst.size() == size); + Randomize(dst); + FnImpl(dst, src, size); + for (size_t i = 0; i < size; ++i) + if (dst[i] != src[i]) + return false; + return true; +} + +template +bool CheckMemset(cpp::span dst, uint8_t value, size_t size) { + Randomize(dst); + FnImpl(dst, value, size); + for (char c : dst) + if (c != (char)value) + return false; + return true; +} + +template +bool CheckBcmp(cpp::span span1, cpp::span span2, size_t size) { + assert(span1.size() == span2.size()); + Copy(span2, span1); + // Compare equal + if (int cmp = FnImpl(span1, span2, size); cmp != 0) + return false; + // Compare not equal if any byte differs + for (size_t i = 0; i < size; ++i) { + ++span2[i]; + if (int cmp = FnImpl(span1, span2, size); cmp == 0) + return false; + if (int cmp = FnImpl(span2, span1, size); cmp == 0) + return false; + --span2[i]; + } + return true; +} + +template +bool CheckMemcmp(cpp::span span1, cpp::span span2, size_t size) { + assert(span1.size() == span2.size()); + Copy(span2, span1); + // Compare equal + if (int cmp = FnImpl(span1, span2, size); cmp != 0) + return false; + // Compare not equal if any byte differs + for (size_t i = 0; i < size; ++i) { + ++span2[i]; + int ground_truth = __builtin_memcmp(span1.data(), span2.data(), size); + if (ground_truth > 0) { + if (int cmp = FnImpl(span1, span2, size); cmp <= 0) + return false; + if (int cmp = FnImpl(span2, span1, size); cmp >= 0) + return false; + } else { + if (int cmp = FnImpl(span1, span2, size); cmp >= 0) + return false; + if (int cmp = FnImpl(span2, span1, size); cmp <= 0) + return false; + } + --span2[i]; + } + return true; +} + +} // namespace __llvm_libc +#endif // LIBC_TEST_SRC_STRING_MEMORY_UTILS_MEMORY_CHECK_UTILS_H diff --git a/libc/test/src/string/memory_utils/op_tests.cpp b/libc/test/src/string/memory_utils/op_tests.cpp --- a/libc/test/src/string/memory_utils/op_tests.cpp +++ b/libc/test/src/string/memory_utils/op_tests.cpp @@ -6,29 +6,14 @@ // //===----------------------------------------------------------------------===// -#include "src/__support/CPP/limits.h" -#include "src/__support/CPP/span.h" +#include "memory_check_utils.h" #include "src/string/memory_utils/op_aarch64.h" #include "src/string/memory_utils/op_builtin.h" #include "src/string/memory_utils/op_generic.h" #include "src/string/memory_utils/op_x86.h" -#include "src/string/memory_utils/utils.h" #include "utils/UnitTest/Test.h" #include -#include - -// User code should use macros instead of functions. -#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) -#include -#define ASAN_POISON_MEMORY_REGION(addr, size) \ - __asan_poison_memory_region((addr), (size)) -#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ - __asan_unpoison_memory_region((addr), (size)) -#else -#define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) -#define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) -#endif #if defined(LLVM_LIBC_ARCH_X86_64) || defined(LLVM_LIBC_ARCH_AARCH64) #define LLVM_LIBC_HAS_UINT64 @@ -36,70 +21,6 @@ namespace __llvm_libc { -static char GetRandomChar() { - static constexpr const uint64_t a = 1103515245; - static constexpr const uint64_t c = 12345; - static constexpr const uint64_t m = 1ULL << 31; - static uint64_t seed = 123456789; - seed = (a * seed + c) % m; - return seed; -} - -// Randomize the content of the buffer. -static void Randomize(cpp::span buffer) { - for (auto ¤t : buffer) - current = GetRandomChar(); -} - -// Copy one span to another. -static void Copy(cpp::span dst, const cpp::span src) { - assert(dst.size() == src.size()); - for (size_t i = 0; i < dst.size(); ++i) - dst[i] = src[i]; -} - -cpp::byte *as_byte(cpp::span span) { - return reinterpret_cast(span.data()); -} - -// Simple structure to allocate a buffer of a particular size. -struct PoisonedBuffer { - PoisonedBuffer(size_t size) : ptr((char *)malloc(size)) { - assert(ptr); - ASAN_POISON_MEMORY_REGION(ptr, size); - } - ~PoisonedBuffer() { free(ptr); } - -protected: - char *ptr = nullptr; -}; - -// Simple structure to allocate a buffer (aligned or not) of a particular size. -// It is backed by a wider buffer that is marked poisoned when ASAN is present. -// The requested region is unpoisoned, this allows catching out of bounds -// accesses. -enum class Aligned : bool { NO = false, YES = true }; -struct Buffer : private PoisonedBuffer { - static constexpr size_t kAlign = 64; - static constexpr size_t kLeeway = 2 * kAlign; - Buffer(size_t size, Aligned aligned = Aligned::YES) - : PoisonedBuffer(size + kLeeway), size(size) { - offset_ptr = ptr; - offset_ptr += distance_to_next_aligned(ptr); - assert((uintptr_t)(offset_ptr) % kAlign == 0); - if (aligned == Aligned::NO) - ++offset_ptr; - assert(offset_ptr > ptr); - assert((offset_ptr + size) < (ptr + size + kLeeway)); - ASAN_UNPOISON_MEMORY_REGION(offset_ptr, size); - } - cpp::span span() { return cpp::span(offset_ptr, size); } - -private: - size_t size = 0; - char *offset_ptr = nullptr; -}; - // Allocates two Buffer and extracts two spans out of them, one // aligned and one misaligned. Tests are run on both spans. struct Buffers { @@ -130,20 +51,17 @@ #endif // LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE >; +static inline cpp::byte *as_byte(cpp::span span) { + return reinterpret_cast(span.data()); +} + template -bool CheckMemcpy(cpp::span dst, cpp::span src, size_t size) { - assert(dst.size() == src.size()); - assert(dst.size() == size); - Randomize(dst); +void SpanCopyAdaptor(cpp::span dst, cpp::span src, size_t size) { FnImpl(as_byte(dst), as_byte(src), size); - for (size_t i = 0; i < size; ++i) - if (dst[i] != src[i]) - return false; - return true; } template -static void MemcpyAdaptor(Ptr dst, CPtr src, size_t size) { +static void BlockCopyAdaptor(Ptr dst, CPtr src, size_t size) { assert(size == T::SIZE); return T::block(dst, src); } @@ -152,34 +70,37 @@ using Impl = ParamType; constexpr size_t kSize = Impl::SIZE; { // Test block operation + static constexpr auto BlockImpl = SpanCopyAdaptor>; Buffers SrcBuffer(kSize); Buffers DstBuffer(kSize); for (auto src : SrcBuffer.spans()) { Randomize(src); for (auto dst : DstBuffer.spans()) { - ASSERT_TRUE(CheckMemcpy>(dst, src, kSize)); + ASSERT_TRUE(CheckMemcpy(dst, src, kSize)); } } } { // Test head tail operations from kSize to 2 * kSize. + static constexpr auto HeadTailImpl = SpanCopyAdaptor; Buffer SrcBuffer(2 * kSize); Buffer DstBuffer(2 * kSize); Randomize(SrcBuffer.span()); for (size_t size = kSize; size < 2 * kSize; ++size) { auto src = SrcBuffer.span().subspan(0, size); auto dst = DstBuffer.span().subspan(0, size); - ASSERT_TRUE(CheckMemcpy(dst, src, size)); + ASSERT_TRUE(CheckMemcpy(dst, src, size)); } } { // Test loop operations from kSize to 3 * kSize. if constexpr (kSize > 1) { + static constexpr auto LoopImpl = SpanCopyAdaptor; Buffer SrcBuffer(3 * kSize); Buffer DstBuffer(3 * kSize); Randomize(SrcBuffer.span()); for (size_t size = kSize; size < 3 * kSize; ++size) { auto src = SrcBuffer.span().subspan(0, size); auto dst = DstBuffer.span().subspan(0, size); - ASSERT_TRUE(CheckMemcpy(dst, src, size)); + ASSERT_TRUE(CheckMemcpy(dst, src, size)); } } } @@ -217,48 +138,46 @@ generic::Memset<64, 32> // >; -template -bool CheckMemset(cpp::span dst, uint8_t value, size_t size) { - Randomize(dst); - FnImpl(as_byte(dst), value, size); - for (char c : dst) - if (c != (char)value) - return false; - return true; -} - template -static void MemsetAdaptor(Ptr dst, uint8_t value, size_t size) { +static void BlockSetAdaptor(Ptr dst, uint8_t value, size_t size) { assert(size == T::SIZE); return T::block(dst, value); } +template +void SpanSetAdaptor(cpp::span dst, uint8_t value, size_t size) { + FnImpl(as_byte(dst), value, size); +} + TYPED_TEST(LlvmLibcOpTest, Memset, MemsetImplementations) { using Impl = ParamType; constexpr size_t kSize = Impl::SIZE; { // Test block operation + static constexpr auto BlockImpl = SpanSetAdaptor>; Buffers DstBuffer(kSize); for (uint8_t value : cpp::array{0, 1, 255}) { for (auto dst : DstBuffer.spans()) { - ASSERT_TRUE(CheckMemset>(dst, value, kSize)); + ASSERT_TRUE(CheckMemset(dst, value, kSize)); } } } { // Test head tail operations from kSize to 2 * kSize. + static constexpr auto HeadTailImpl = SpanSetAdaptor; Buffer DstBuffer(2 * kSize); for (size_t size = kSize; size < 2 * kSize; ++size) { const char value = size % 10; auto dst = DstBuffer.span().subspan(0, size); - ASSERT_TRUE(CheckMemset(dst, value, size)); + ASSERT_TRUE(CheckMemset(dst, value, size)); } } { // Test loop operations from kSize to 3 * kSize. if constexpr (kSize > 1) { + static constexpr auto LoopImpl = SpanSetAdaptor; Buffer DstBuffer(3 * kSize); for (size_t size = kSize; size < 3 * kSize; ++size) { const char value = size % 10; auto dst = DstBuffer.span().subspan(0, size); - ASSERT_TRUE((CheckMemset(dst, value, size))); + ASSERT_TRUE((CheckMemset(dst, value, size))); } } } @@ -295,62 +214,51 @@ generic::Bcmp<64> // >; -template -bool CheckBcmp(cpp::span span1, cpp::span span2, size_t size) { - assert(span1.size() == span2.size()); - Copy(span2, span1); - // Compare equal - if (int cmp = (int)FnImpl(as_byte(span1), as_byte(span2), size); cmp != 0) - return false; - // Compare not equal if any byte differs - for (size_t i = 0; i < size; ++i) { - ++span2[i]; - if (int cmp = (int)FnImpl(as_byte(span1), as_byte(span2), size); cmp == 0) - return false; - if (int cmp = (int)FnImpl(as_byte(span2), as_byte(span1), size); cmp == 0) - return false; - --span2[i]; - } - return true; -} - template -static BcmpReturnType BcmpAdaptor(CPtr p1, CPtr p2, size_t size) { +static auto BlockCmpAdaptor(CPtr p1, CPtr p2, size_t size) { assert(size == T::SIZE); return T::block(p1, p2); } +template +int SpanCmpAdaptor(cpp::span p1, cpp::span p2, size_t size) { + return (int)FnImpl(as_byte(p1), as_byte(p2), size); +} + TYPED_TEST(LlvmLibcOpTest, Bcmp, BcmpImplementations) { using Impl = ParamType; constexpr size_t kSize = Impl::SIZE; { // Test block operation + static constexpr auto BlockImpl = SpanCmpAdaptor>; Buffers Buffer1(kSize); Buffers Buffer2(kSize); for (auto span1 : Buffer1.spans()) { Randomize(span1); for (auto span2 : Buffer2.spans()) - ASSERT_TRUE((CheckBcmp>(span1, span2, kSize))); + ASSERT_TRUE((CheckBcmp(span1, span2, kSize))); } } { // Test head tail operations from kSize to 2 * kSize. + static constexpr auto HeadTailImpl = SpanCmpAdaptor; Buffer Buffer1(2 * kSize); Buffer Buffer2(2 * kSize); Randomize(Buffer1.span()); for (size_t size = kSize; size < 2 * kSize; ++size) { auto span1 = Buffer1.span().subspan(0, size); auto span2 = Buffer2.span().subspan(0, size); - ASSERT_TRUE((CheckBcmp(span1, span2, size))); + ASSERT_TRUE((CheckBcmp(span1, span2, size))); } } { // Test loop operations from kSize to 3 * kSize. if constexpr (kSize > 1) { + static constexpr auto LoopImpl = SpanCmpAdaptor; Buffer Buffer1(3 * kSize); Buffer Buffer2(3 * kSize); Randomize(Buffer1.span()); for (size_t size = kSize; size < 3 * kSize; ++size) { auto span1 = Buffer1.span().subspan(0, size); auto span2 = Buffer2.span().subspan(0, size); - ASSERT_TRUE((CheckBcmp(span1, span2, size))); + ASSERT_TRUE((CheckBcmp(span1, span2, size))); } } } @@ -384,33 +292,6 @@ generic::Memcmp<64> // >; -template -bool CheckMemcmp(cpp::span span1, cpp::span span2, size_t size) { - assert(span1.size() == span2.size()); - Copy(span2, span1); - // Compare equal - if (int cmp = (int)FnImpl(as_byte(span1), as_byte(span2), size); cmp != 0) - return false; - // Compare not equal if any byte differs - for (size_t i = 0; i < size; ++i) { - ++span2[i]; - int ground_truth = __builtin_memcmp(span1.data(), span2.data(), size); - if (ground_truth > 0) { - if (int cmp = (int)FnImpl(as_byte(span1), as_byte(span2), size); cmp <= 0) - return false; - if (int cmp = (int)FnImpl(as_byte(span2), as_byte(span1), size); cmp >= 0) - return false; - } else { - if (int cmp = (int)FnImpl(as_byte(span1), as_byte(span2), size); cmp >= 0) - return false; - if (int cmp = (int)FnImpl(as_byte(span2), as_byte(span1), size); cmp <= 0) - return false; - } - --span2[i]; - } - return true; -} - template static MemcmpReturnType MemcmpAdaptor(CPtr p1, CPtr p2, size_t size) { assert(size == T::SIZE); @@ -421,33 +302,36 @@ using Impl = ParamType; constexpr size_t kSize = Impl::SIZE; { // Test block operation + static constexpr auto BlockImpl = SpanCmpAdaptor>; Buffers Buffer1(kSize); Buffers Buffer2(kSize); for (auto span1 : Buffer1.spans()) { Randomize(span1); for (auto span2 : Buffer2.spans()) - ASSERT_TRUE((CheckMemcmp>(span1, span2, kSize))); + ASSERT_TRUE((CheckMemcmp(span1, span2, kSize))); } } { // Test head tail operations from kSize to 2 * kSize. + static constexpr auto HeadTailImpl = SpanCmpAdaptor; Buffer Buffer1(2 * kSize); Buffer Buffer2(2 * kSize); Randomize(Buffer1.span()); for (size_t size = kSize; size < 2 * kSize; ++size) { auto span1 = Buffer1.span().subspan(0, size); auto span2 = Buffer2.span().subspan(0, size); - ASSERT_TRUE((CheckMemcmp(span1, span2, size))); + ASSERT_TRUE((CheckMemcmp(span1, span2, size))); } } { // Test loop operations from kSize to 3 * kSize. if constexpr (kSize > 1) { + static constexpr auto LoopImpl = SpanCmpAdaptor; Buffer Buffer1(3 * kSize); Buffer Buffer2(3 * kSize); Randomize(Buffer1.span()); for (size_t size = kSize; size < 3 * kSize; ++size) { auto span1 = Buffer1.span().subspan(0, size); auto span2 = Buffer2.span().subspan(0, size); - ASSERT_TRUE((CheckMemcmp(span1, span2, size))); + ASSERT_TRUE((CheckMemcmp(span1, span2, size))); } } } diff --git a/libc/test/src/string/memset_test.cpp b/libc/test/src/string/memset_test.cpp --- a/libc/test/src/string/memset_test.cpp +++ b/libc/test/src/string/memset_test.cpp @@ -6,48 +6,26 @@ // //===----------------------------------------------------------------------===// -#include "src/__support/CPP/span.h" +#include "memory_utils/memory_check_utils.h" #include "src/string/memset.h" #include "utils/UnitTest/Test.h" -using __llvm_libc::cpp::array; -using __llvm_libc::cpp::span; -using Data = array; +namespace __llvm_libc { -static const span k_deadcode("DEADC0DE", 8); - -// Returns a Data object filled with a repetition of `filler`. -Data get_data(span filler) { - Data out; - for (size_t i = 0; i < out.size(); ++i) - out[i] = filler[i % filler.size()]; - return out; +template +void SpanSetAdaptor(cpp::span p1, uint8_t value, size_t size) { + FnImpl(p1.begin(), value, size); } TEST(LlvmLibcMemsetTest, Thorough) { - const Data dirty = get_data(k_deadcode); - for (int value = -1; value <= 1; ++value) { - for (size_t count = 0; count < 1024; ++count) { - for (size_t align = 0; align < 64; ++align) { - auto buffer = dirty; - void *const dst = &buffer[align]; - void *const ret = __llvm_libc::memset(dst, value, count); - // Return value is `dst`. - ASSERT_EQ(ret, dst); - // Everything before copy is untouched. - for (size_t i = 0; i < align; ++i) - ASSERT_EQ(buffer[i], dirty[i]); - // Everything in between is copied. - for (size_t i = 0; i < count; ++i) - ASSERT_EQ(buffer[align + i], (char)value); - // Everything after copy is untouched. - for (size_t i = align + count; i < dirty.size(); ++i) - ASSERT_EQ(buffer[i], dirty[i]); - } - } + static constexpr size_t kSize = 1024; + static constexpr auto Impl = SpanSetAdaptor<__llvm_libc::memset>; + Buffer DstBuffer(kSize); + for (size_t size = 0; size < kSize; ++size) { + const char value = size % 10; + auto dst = DstBuffer.span().subspan(0, size); + ASSERT_TRUE((CheckMemset(dst, value, size))); } } -// FIXME: Add tests with reads and writes on the boundary of a read/write -// protected page to check we're not reading nor writing prior/past the allowed -// regions. +} // namespace __llvm_libc