[libc-commits] [libc] 3635195 - [libc] Improve testing of mem functions

Guillaume Chatelet via libc-commits libc-commits at lists.llvm.org
Wed Nov 2 01:56:01 PDT 2022


Author: Guillaume Chatelet
Date: 2022-11-02T08:55:46Z
New Revision: 3635195e0d54162ba85124fdefcac39bff703149

URL: https://github.com/llvm/llvm-project/commit/3635195e0d54162ba85124fdefcac39bff703149
DIFF: https://github.com/llvm/llvm-project/commit/3635195e0d54162ba85124fdefcac39bff703149.diff

LOG: [libc] Improve testing of mem functions

This patch extracts the testing logic from `op_tests.cpp` into
`memory_check_utils.h` so we can reuse it for mem* function integration
tests.

This makes testing consistent and thorough.
For instance this catches a bug that got unnoticed during submission of
D136595 and D135134. Integration test for memcmp was only testing a
single size.

This also leverages ASAN to make sure that data is not read / written
outside permitted boundaries

Differential Revision: https://reviews.llvm.org/D136865

Added: 
    libc/test/src/string/memory_utils/memory_check_utils.h

Modified: 
    libc/test/src/string/bcmp_test.cpp
    libc/test/src/string/bzero_test.cpp
    libc/test/src/string/memcmp_test.cpp
    libc/test/src/string/memcpy_test.cpp
    libc/test/src/string/memmove_test.cpp
    libc/test/src/string/memory_utils/op_tests.cpp
    libc/test/src/string/memset_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/test/src/string/bcmp_test.cpp b/libc/test/src/string/bcmp_test.cpp
index 8f0fe5262f22d..5f0bb0fc932fa 100644
--- 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,23 @@ TEST(LlvmLibcBcmpTest, LhsAfterRhsLexically) {
   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';
+// Adapt CheckBcmp signature to op implementation signatures.
+template <auto FnImpl>
+int CmpAdaptor(cpp::span<char> p1, cpp::span<char> p2, size_t size) {
+  return FnImpl(p1.begin(), p2.begin(), size);
+}
+
+TEST(LlvmLibcBcmpTest, SizeSweep) {
+  static constexpr size_t kMaxSize = 1024;
+  static constexpr auto Impl = CmpAdaptor<__llvm_libc::bcmp>;
+  Buffer Buffer1(kMaxSize);
+  Buffer Buffer2(kMaxSize);
+  Randomize(Buffer1.span());
+  for (size_t size = 0; size < kMaxSize; ++size) {
+    auto span1 = Buffer1.span().subspan(0, size);
+    auto span2 = Buffer2.span().subspan(0, size);
+    ASSERT_TRUE((CheckBcmp<Impl>(span1, span2, size)));
   }
 }
+
+} // namespace __llvm_libc

diff  --git a/libc/test/src/string/bzero_test.cpp b/libc/test/src/string/bzero_test.cpp
index 5ae3a497ab2ca..1f4be711edc44 100644
--- a/libc/test/src/string/bzero_test.cpp
+++ b/libc/test/src/string/bzero_test.cpp
@@ -6,44 +6,27 @@
 //
 //===----------------------------------------------------------------------===//
 
-#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<char, 2048>;
+namespace __llvm_libc {
 
-static const span<const char> k_deadcode("DEADC0DE", 8);
-
-// Returns a Data object filled with a repetition of `filler`.
-Data get_data(span<const char> filler) {
-  Data out;
-  for (size_t i = 0; i < out.size(); ++i)
-    out[i] = filler[i % filler.size()];
-  return out;
+// Adapt CheckMemset signature to op implementation signatures.
+template <auto FnImpl>
+void BzeroAdaptor(cpp::span<char> 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]);
-    }
+TEST(LlvmLibcBzeroTest, SizeSweep) {
+  static constexpr size_t kMaxSize = 1024;
+  static constexpr auto Impl = BzeroAdaptor<__llvm_libc::bzero>;
+  Buffer DstBuffer(kMaxSize);
+  for (size_t size = 0; size < kMaxSize; ++size) {
+    auto dst = DstBuffer.span().subspan(0, size);
+    ASSERT_TRUE((CheckMemset<Impl>(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
index ab798012342e8..9056851add960 100644
--- 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,23 @@ TEST(LlvmLibcMemcmpTest, LhsAfterRhsLexically) {
   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';
+// Adapt CheckMemcmp signature to op implementation signatures.
+template <auto FnImpl>
+int CmpAdaptor(cpp::span<char> p1, cpp::span<char> p2, size_t size) {
+  return FnImpl(p1.begin(), p2.begin(), size);
+}
+
+TEST(LlvmLibcMemcmpTest, SizeSweep) {
+  static constexpr size_t kMaxSize = 1024;
+  static constexpr auto Impl = CmpAdaptor<__llvm_libc::memcmp>;
+  Buffer Buffer1(kMaxSize);
+  Buffer Buffer2(kMaxSize);
+  Randomize(Buffer1.span());
+  for (size_t size = 0; size < kMaxSize; ++size) {
+    auto span1 = Buffer1.span().subspan(0, size);
+    auto span2 = Buffer2.span().subspan(0, size);
+    ASSERT_TRUE((CheckMemcmp<Impl>(span1, span2, size)));
   }
 }
+
+} // namespace __llvm_libc

diff  --git a/libc/test/src/string/memcpy_test.cpp b/libc/test/src/string/memcpy_test.cpp
index 5ccbb4dc45598..99631630f2645 100644
--- a/libc/test/src/string/memcpy_test.cpp
+++ b/libc/test/src/string/memcpy_test.cpp
@@ -6,49 +6,29 @@
 //
 //===----------------------------------------------------------------------===//
 
-#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<char, 2048>;
+namespace __llvm_libc {
 
-static const span<const char> k_numbers("0123456789", 10);
-static const span<const char> k_deadcode("DEADC0DE", 8);
-
-// Returns a Data object filled with a repetition of `filler`.
-Data get_data(span<const char> filler) {
-  Data out;
-  for (size_t i = 0; i < out.size(); ++i)
-    out[i] = filler[i % filler.size()];
-  return out;
+// Adapt CheckMemcpy signature to op implementation signatures.
+template <auto FnImpl>
+void CopyAdaptor(cpp::span<char> dst, cpp::span<char> 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]);
-    }
+TEST(LlvmLibcMemcpyTest, SizeSweep) {
+  static constexpr size_t kMaxSize = 1024;
+  static constexpr auto Impl = CopyAdaptor<__llvm_libc::memcpy>;
+  Buffer SrcBuffer(kMaxSize);
+  Buffer DstBuffer(kMaxSize);
+  Randomize(SrcBuffer.span());
+  for (size_t size = 0; size < kMaxSize; ++size) {
+    auto src = SrcBuffer.span().subspan(0, size);
+    auto dst = DstBuffer.span().subspan(0, size);
+    ASSERT_TRUE(CheckMemcpy<Impl>(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/memmove_test.cpp b/libc/test/src/string/memmove_test.cpp
index 451ccdb0a89b9..26c193620b453 100644
--- a/libc/test/src/string/memmove_test.cpp
+++ b/libc/test/src/string/memmove_test.cpp
@@ -90,7 +90,7 @@ void Randomize(span<char> Buffer) {
     current = GetRandomChar();
 }
 
-TEST(LlvmLibcMemmoveTest, Thorough) {
+TEST(LlvmLibcMemmoveTest, SizeSweep) {
   using LargeBuffer = array<char, 3 * kMaxSize>;
   LargeBuffer GroundTruth;
   Randomize(GroundTruth);

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
index 0000000000000..19cd01045bcb8
--- /dev/null
+++ b/libc/test/src/string/memory_utils/memory_check_utils.h
@@ -0,0 +1,171 @@
+//===-- 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.h> // assert
+#include <stddef.h> // size_t
+#include <stdint.h> // uintxx_t
+#include <stdlib.h> // malloc/free
+
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+#include <sanitizer/asan_interface.h>
+#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.
+// When ASAN is present it also poisons the whole memory.
+// This is a utility class to be used by Buffer below, do not use directly.
+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<kAlign>(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<char> span() { return cpp::span<char>(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<char> buffer) {
+  for (auto &current : buffer)
+    current = GetRandomChar();
+}
+
+// Copy one span to another.
+__attribute__((no_builtin)) static inline void
+ReferenceCopy(cpp::span<char> dst, const cpp::span<char> src) {
+  assert(dst.size() == src.size());
+  for (size_t i = 0; i < dst.size(); ++i)
+    dst[i] = src[i];
+}
+
+// Checks that FnImpl implements the memcpy semantic.
+template <auto FnImpl>
+bool CheckMemcpy(cpp::span<char> dst, cpp::span<char> 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;
+}
+
+// Checks that FnImpl implements the memset semantic.
+template <auto FnImpl>
+bool CheckMemset(cpp::span<char> 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;
+}
+
+// Checks that FnImpl implements the bcmp semantic.
+template <auto FnImpl>
+bool CheckBcmp(cpp::span<char> span1, cpp::span<char> span2, size_t size) {
+  assert(span1.size() == span2.size());
+  ReferenceCopy(span2, span1);
+  // Compare equal
+  if (int cmp = FnImpl(span1, span2, size); cmp != 0)
+    return false;
+  // Compare not equal if any byte 
diff ers
+  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;
+}
+
+// Checks that FnImpl implements the memcmp semantic.
+template <auto FnImpl>
+bool CheckMemcmp(cpp::span<char> span1, cpp::span<char> span2, size_t size) {
+  assert(span1.size() == span2.size());
+  ReferenceCopy(span2, span1);
+  // Compare equal
+  if (int cmp = FnImpl(span1, span2, size); cmp != 0)
+    return false;
+  // Compare not equal if any byte 
diff ers
+  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;
+}
+
+// TODO: Also implement the memmove semantic
+
+} // 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
index 715c61152e1be..fdfe9d92ce096 100644
--- 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 <assert.h>
-#include <stdlib.h>
-
-// User code should use macros instead of functions.
-#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
-#include <sanitizer/asan_interface.h>
-#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<char> buffer) {
-  for (auto &current : buffer)
-    current = GetRandomChar();
-}
-
-// Copy one span to another.
-static void Copy(cpp::span<char> dst, const cpp::span<char> 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<char> span) {
-  return reinterpret_cast<cpp::byte *>(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<kAlign>(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<char> span() { return cpp::span<char>(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,56 +51,57 @@ using MemcpyImplementations = testing::TypeList<
 #endif // LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE
     >;
 
+// Convenient helper to turn a span into cpp::byte *.
+static inline cpp::byte *as_byte(cpp::span<char> span) {
+  return reinterpret_cast<cpp::byte *>(span.data());
+}
+
+// Adapt CheckMemcpy signature to op implementation signatures.
 template <auto FnImpl>
-bool CheckMemcpy(cpp::span<char> dst, cpp::span<char> src, size_t size) {
-  assert(dst.size() == src.size());
-  assert(dst.size() == size);
-  Randomize(dst);
+void CopyAdaptor(cpp::span<char> dst, cpp::span<char> 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 <typename T>
-static void MemcpyAdaptor(Ptr dst, CPtr src, size_t size) {
-  assert(size == T::SIZE);
-  return T::block(dst, src);
+template <size_t Size, auto FnImpl>
+void CopyBlockAdaptor(cpp::span<char> dst, cpp::span<char> src, size_t size) {
+  assert(size == Size);
+  FnImpl(as_byte(dst), as_byte(src));
 }
 
 TYPED_TEST(LlvmLibcOpTest, Memcpy, MemcpyImplementations) {
   using Impl = ParamType;
   constexpr size_t kSize = Impl::SIZE;
   { // Test block operation
+    static constexpr auto BlockImpl = CopyBlockAdaptor<kSize, Impl::block>;
     Buffers SrcBuffer(kSize);
     Buffers DstBuffer(kSize);
     for (auto src : SrcBuffer.spans()) {
       Randomize(src);
       for (auto dst : DstBuffer.spans()) {
-        ASSERT_TRUE(CheckMemcpy<MemcpyAdaptor<Impl>>(dst, src, kSize));
+        ASSERT_TRUE(CheckMemcpy<BlockImpl>(dst, src, kSize));
       }
     }
   }
   { // Test head tail operations from kSize to 2 * kSize.
+    static constexpr auto HeadTailImpl = CopyAdaptor<Impl::head_tail>;
     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<Impl::head_tail>(dst, src, size));
+      ASSERT_TRUE(CheckMemcpy<HeadTailImpl>(dst, src, size));
     }
   }
   { // Test loop operations from kSize to 3 * kSize.
     if constexpr (kSize > 1) {
+      static constexpr auto LoopImpl = CopyAdaptor<Impl::loop_and_tail>;
       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<Impl::loop_and_tail>(dst, src, size));
+        ASSERT_TRUE(CheckMemcpy<LoopImpl>(dst, src, size));
       }
     }
   }
@@ -217,48 +139,46 @@ using MemsetImplementations = testing::TypeList<
     generic::Memset<64, 32>  //
     >;
 
+// Adapt CheckMemset signature to op implementation signatures.
 template <auto FnImpl>
-bool CheckMemset(cpp::span<char> dst, uint8_t value, size_t size) {
-  Randomize(dst);
+void SetAdaptor(cpp::span<char> dst, uint8_t value, size_t size) {
   FnImpl(as_byte(dst), value, size);
-  for (char c : dst)
-    if (c != (char)value)
-      return false;
-  return true;
 }
-
-template <typename T>
-static void MemsetAdaptor(Ptr dst, uint8_t value, size_t size) {
-  assert(size == T::SIZE);
-  return T::block(dst, value);
+template <size_t Size, auto FnImpl>
+void SetBlockAdaptor(cpp::span<char> dst, uint8_t value, size_t size) {
+  assert(size == Size);
+  FnImpl(as_byte(dst), value);
 }
 
 TYPED_TEST(LlvmLibcOpTest, Memset, MemsetImplementations) {
   using Impl = ParamType;
   constexpr size_t kSize = Impl::SIZE;
   { // Test block operation
+    static constexpr auto BlockImpl = SetBlockAdaptor<kSize, Impl::block>;
     Buffers DstBuffer(kSize);
     for (uint8_t value : cpp::array<uint8_t, 3>{0, 1, 255}) {
       for (auto dst : DstBuffer.spans()) {
-        ASSERT_TRUE(CheckMemset<MemsetAdaptor<Impl>>(dst, value, kSize));
+        ASSERT_TRUE(CheckMemset<BlockImpl>(dst, value, kSize));
       }
     }
   }
   { // Test head tail operations from kSize to 2 * kSize.
+    static constexpr auto HeadTailImpl = SetAdaptor<Impl::head_tail>;
     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<Impl::head_tail>(dst, value, size));
+      ASSERT_TRUE(CheckMemset<HeadTailImpl>(dst, value, size));
     }
   }
   { // Test loop operations from kSize to 3 * kSize.
     if constexpr (kSize > 1) {
+      static constexpr auto LoopImpl = SetAdaptor<Impl::loop_and_tail>;
       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<Impl::loop_and_tail>(dst, value, size)));
+        ASSERT_TRUE((CheckMemset<LoopImpl>(dst, value, size)));
       }
     }
   }
@@ -295,62 +215,51 @@ using BcmpImplementations = testing::TypeList<
     generic::Bcmp<64>  //
     >;
 
+// Adapt CheckBcmp signature to op implementation signatures.
 template <auto FnImpl>
-bool CheckBcmp(cpp::span<char> span1, cpp::span<char> 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 
diff ers
-  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;
+int CmpAdaptor(cpp::span<char> p1, cpp::span<char> p2, size_t size) {
+  return (int)FnImpl(as_byte(p1), as_byte(p2), size);
 }
-
-template <typename T>
-static BcmpReturnType BcmpAdaptor(CPtr p1, CPtr p2, size_t size) {
-  assert(size == T::SIZE);
-  return T::block(p1, p2);
+template <size_t Size, auto FnImpl>
+int CmpBlockAdaptor(cpp::span<char> p1, cpp::span<char> p2, size_t size) {
+  assert(size == Size);
+  return (int)FnImpl(as_byte(p1), as_byte(p2));
 }
 
 TYPED_TEST(LlvmLibcOpTest, Bcmp, BcmpImplementations) {
   using Impl = ParamType;
   constexpr size_t kSize = Impl::SIZE;
   { // Test block operation
+    static constexpr auto BlockImpl = CmpBlockAdaptor<kSize, Impl::block>;
     Buffers Buffer1(kSize);
     Buffers Buffer2(kSize);
     for (auto span1 : Buffer1.spans()) {
       Randomize(span1);
       for (auto span2 : Buffer2.spans())
-        ASSERT_TRUE((CheckBcmp<BcmpAdaptor<Impl>>(span1, span2, kSize)));
+        ASSERT_TRUE((CheckBcmp<BlockImpl>(span1, span2, kSize)));
     }
   }
   { // Test head tail operations from kSize to 2 * kSize.
+    static constexpr auto HeadTailImpl = CmpAdaptor<Impl::head_tail>;
     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<Impl::head_tail>(span1, span2, size)));
+      ASSERT_TRUE((CheckBcmp<HeadTailImpl>(span1, span2, size)));
     }
   }
   { // Test loop operations from kSize to 3 * kSize.
     if constexpr (kSize > 1) {
+      static constexpr auto LoopImpl = CmpAdaptor<Impl::loop_and_tail>;
       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<Impl::loop_and_tail>(span1, span2, size)));
+        ASSERT_TRUE((CheckBcmp<LoopImpl>(span1, span2, size)));
       }
     }
   }
@@ -384,70 +293,40 @@ using MemcmpImplementations = testing::TypeList<
     generic::Memcmp<64>  //
     >;
 
-template <auto FnImpl>
-bool CheckMemcmp(cpp::span<char> span1, cpp::span<char> 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 
diff ers
-  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 <typename T>
-static MemcmpReturnType MemcmpAdaptor(CPtr p1, CPtr p2, size_t size) {
-  assert(size == T::SIZE);
-  return T::block(p1, p2);
-}
-
 TYPED_TEST(LlvmLibcOpTest, Memcmp, MemcmpImplementations) {
   using Impl = ParamType;
   constexpr size_t kSize = Impl::SIZE;
   { // Test block operation
+    static constexpr auto BlockImpl = CmpBlockAdaptor<kSize, Impl::block>;
     Buffers Buffer1(kSize);
     Buffers Buffer2(kSize);
     for (auto span1 : Buffer1.spans()) {
       Randomize(span1);
       for (auto span2 : Buffer2.spans())
-        ASSERT_TRUE((CheckMemcmp<MemcmpAdaptor<Impl>>(span1, span2, kSize)));
+        ASSERT_TRUE((CheckMemcmp<BlockImpl>(span1, span2, kSize)));
     }
   }
   { // Test head tail operations from kSize to 2 * kSize.
+    static constexpr auto HeadTailImpl = CmpAdaptor<Impl::head_tail>;
     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<Impl::head_tail>(span1, span2, size)));
+      ASSERT_TRUE((CheckMemcmp<HeadTailImpl>(span1, span2, size)));
     }
   }
   { // Test loop operations from kSize to 3 * kSize.
     if constexpr (kSize > 1) {
+      static constexpr auto LoopImpl = CmpAdaptor<Impl::loop_and_tail>;
       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<Impl::loop_and_tail>(span1, span2, size)));
+        ASSERT_TRUE((CheckMemcmp<LoopImpl>(span1, span2, size)));
       }
     }
   }

diff  --git a/libc/test/src/string/memset_test.cpp b/libc/test/src/string/memset_test.cpp
index 24c7c44a1649f..1b484ef1f06a4 100644
--- a/libc/test/src/string/memset_test.cpp
+++ b/libc/test/src/string/memset_test.cpp
@@ -6,48 +6,27 @@
 //
 //===----------------------------------------------------------------------===//
 
-#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<char, 2048>;
+namespace __llvm_libc {
 
-static const span<const char> k_deadcode("DEADC0DE", 8);
-
-// Returns a Data object filled with a repetition of `filler`.
-Data get_data(span<const char> filler) {
-  Data out;
-  for (size_t i = 0; i < out.size(); ++i)
-    out[i] = filler[i % filler.size()];
-  return out;
+// Adapt CheckMemset signature to op implementation signatures.
+template <auto FnImpl>
+void SetAdaptor(cpp::span<char> 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]);
-      }
-    }
+TEST(LlvmLibcMemsetTest, SizeSweep) {
+  static constexpr size_t kMaxSize = 1024;
+  static constexpr auto Impl = SetAdaptor<__llvm_libc::memset>;
+  Buffer DstBuffer(kMaxSize);
+  for (size_t size = 0; size < kMaxSize; ++size) {
+    const char value = size % 10;
+    auto dst = DstBuffer.span().subspan(0, size);
+    ASSERT_TRUE((CheckMemset<Impl>(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


        


More information about the libc-commits mailing list