501 changes: 0 additions & 501 deletions libc/src/string/memory_utils/elements.h

This file was deleted.

151 changes: 0 additions & 151 deletions libc/src/string/memory_utils/elements_x86.h

This file was deleted.

140 changes: 140 additions & 0 deletions libc/src/string/memory_utils/memcpy_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//===-- Memcpy utils --------------------------------------------*- C++ -*-===//
//
// 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_SRC_STRING_MEMORY_UTILS_MEMCPY_UTILS_H
#define LIBC_SRC_STRING_MEMORY_UTILS_MEMCPY_UTILS_H

#include "src/__support/sanitizer.h"
#include "src/string/memory_utils/utils.h"
#include <stddef.h> // size_t

// __builtin_memcpy_inline guarantees to never call external functions.
// Unfortunately it is not widely available.
#ifdef __clang__
#if __has_builtin(__builtin_memcpy_inline)
#define USE_BUILTIN_MEMCPY_INLINE
#endif
#elif defined(__GNUC__)
#define USE_BUILTIN_MEMCPY
#endif

namespace __llvm_libc {

// This is useful for testing.
#if defined(LLVM_LIBC_MEMCPY_MONITOR)
extern "C" void LLVM_LIBC_MEMCPY_MONITOR(char *__restrict,
const char *__restrict, size_t);
#endif

// Copies `kBlockSize` bytes from `src` to `dst` using a for loop.
// This code requires the use of `-fno-buitin-memcpy` to prevent the compiler
// from turning the for-loop back into `__builtin_memcpy`.
template <size_t kBlockSize>
static void ForLoopCopy(char *__restrict dst, const char *__restrict src) {
for (size_t i = 0; i < kBlockSize; ++i)
dst[i] = src[i];
}

// Copies `kBlockSize` bytes from `src` to `dst`.
template <size_t kBlockSize>
static void CopyBlock(char *__restrict dst, const char *__restrict src) {
#if defined(LLVM_LIBC_MEMCPY_MONITOR)
LLVM_LIBC_MEMCPY_MONITOR(dst, src, kBlockSize);
#elif LLVM_LIBC_HAVE_MEMORY_SANITIZER || LLVM_LIBC_HAVE_ADDRESS_SANITIZER
ForLoopCopy<kBlockSize>(dst, src);
#elif defined(USE_BUILTIN_MEMCPY_INLINE)
__builtin_memcpy_inline(dst, src, kBlockSize);
#elif defined(USE_BUILTIN_MEMCPY)
__builtin_memcpy(dst, src, kBlockSize);
#else
ForLoopCopy<kBlockSize>(dst, src);
#endif
}

// Copies `kBlockSize` bytes from `src + count - kBlockSize` to
// `dst + count - kBlockSize`.
// Precondition: `count >= kBlockSize`.
template <size_t kBlockSize>
static void CopyLastBlock(char *__restrict dst, const char *__restrict src,
size_t count) {
const size_t offset = count - kBlockSize;
CopyBlock<kBlockSize>(dst + offset, src + offset);
}

// Copies `kBlockSize` bytes twice with an overlap between the two.
//
// [1234567812345678123]
// [__XXXXXXXXXXXXXX___]
// [__XXXXXXXX_________]
// [________XXXXXXXX___]
//
// Precondition: `count >= kBlockSize && count <= kBlockSize`.
template <size_t kBlockSize>
static void CopyBlockOverlap(char *__restrict dst, const char *__restrict src,
size_t count) {
CopyBlock<kBlockSize>(dst, src);
CopyLastBlock<kBlockSize>(dst, src, count);
}

// Copies `count` bytes by blocks of `kBlockSize` bytes.
// Copies at the start and end of the buffer are unaligned.
// Copies in the middle of the buffer are aligned to `kAlignment`.
//
// e.g. with
// [12345678123456781234567812345678]
// [__XXXXXXXXXXXXXXXXXXXXXXXXXXXX___]
// [__XXXX___________________________]
// [_____XXXXXXXX____________________]
// [_____________XXXXXXXX____________]
// [_____________________XXXXXXXX____]
// [______________________XXXXXXXX___]
//
// Precondition: `kAlignment <= kBlockSize`
// `count > 2 * kBlockSize` for efficiency.
// `count >= kAlignment` for correctness.
template <size_t kBlockSize, size_t kAlignment = kBlockSize>
static void CopySrcAlignedBlocks(char *__restrict dst,
const char *__restrict src, size_t count) {
static_assert(is_power2(kAlignment), "kAlignment must be a power of two");
static_assert(is_power2(kBlockSize), "kBlockSize must be a power of two");
static_assert(kAlignment <= kBlockSize,
"kAlignment must be less or equal to block size");
CopyBlock<kAlignment>(dst, src); // Copy first block

// Copy aligned blocks
const size_t ofla = offset_from_last_aligned<kAlignment>(src);
const size_t limit = count + ofla - kBlockSize;
for (size_t offset = kAlignment; offset < limit; offset += kBlockSize)
CopyBlock<kBlockSize>(dst - ofla + offset,
assume_aligned<kAlignment>(src - ofla + offset));

CopyLastBlock<kBlockSize>(dst, src, count); // Copy last block
}

template <size_t kBlockSize, size_t kAlignment = kBlockSize>
static void CopyDstAlignedBlocks(char *__restrict dst,
const char *__restrict src, size_t count) {
static_assert(is_power2(kAlignment), "kAlignment must be a power of two");
static_assert(is_power2(kBlockSize), "kBlockSize must be a power of two");
static_assert(kAlignment <= kBlockSize,
"kAlignment must be less or equal to block size");
CopyBlock<kAlignment>(dst, src); // Copy first block

// Copy aligned blocks
const size_t ofla = offset_from_last_aligned<kAlignment>(dst);
const size_t limit = count + ofla - kBlockSize;
for (size_t offset = kAlignment; offset < limit; offset += kBlockSize)
CopyBlock<kBlockSize>(assume_aligned<kAlignment>(dst - ofla + offset),
src - ofla + offset);

CopyLastBlock<kBlockSize>(dst, src, count); // Copy last block
}

} // namespace __llvm_libc

#endif // LIBC_SRC_STRING_MEMORY_UTILS_MEMCPY_UTILS_H
83 changes: 68 additions & 15 deletions libc/src/string/memory_utils/memset_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,70 @@
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_MEMSET_UTILS_H
#define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_MEMSET_UTILS_H
#ifndef LIBC_SRC_STRING_MEMORY_UTILS_MEMSET_UTILS_H
#define LIBC_SRC_STRING_MEMORY_UTILS_MEMSET_UTILS_H

#include "src/string/memory_utils/elements.h"
#include "src/string/memory_utils/utils.h"

#include <stddef.h> // size_t

namespace __llvm_libc {

// Sets `kBlockSize` bytes starting from `src` to `value`.
template <size_t kBlockSize> static void SetBlock(char *dst, unsigned value) {
// Theoretically the compiler is allowed to call memset here and end up with a
// recursive call, practically it doesn't happen, however this should be
// replaced with a __builtin_memset_inline once it's available in clang.
__builtin_memset(dst, value, kBlockSize);
}

// Sets `kBlockSize` bytes from `src + count - kBlockSize` to `value`.
// Precondition: `count >= kBlockSize`.
template <size_t kBlockSize>
static void SetLastBlock(char *dst, unsigned value, size_t count) {
SetBlock<kBlockSize>(dst + count - kBlockSize, value);
}

// Sets `kBlockSize` bytes twice with an overlap between the two.
//
// [1234567812345678123]
// [__XXXXXXXXXXXXXX___]
// [__XXXXXXXX_________]
// [________XXXXXXXX___]
//
// Precondition: `count >= kBlockSize && count <= kBlockSize`.
template <size_t kBlockSize>
static void SetBlockOverlap(char *dst, unsigned value, size_t count) {
SetBlock<kBlockSize>(dst, value);
SetLastBlock<kBlockSize>(dst, value, count);
}

// Sets `count` bytes by blocks of `kBlockSize` bytes.
// Sets at the start and end of the buffer are unaligned.
// Sets in the middle of the buffer are aligned to `kBlockSize`.
//
// e.g. with
// [12345678123456781234567812345678]
// [__XXXXXXXXXXXXXXXXXXXXXXXXXXX___]
// [__XXXXXXXX______________________]
// [________XXXXXXXX________________]
// [________________XXXXXXXX________]
// [_____________________XXXXXXXX___]
//
// Precondition: `count > 2 * kBlockSize` for efficiency.
// `count >= kBlockSize` for correctness.
template <size_t kBlockSize>
static void SetAlignedBlocks(char *dst, unsigned value, size_t count) {
SetBlock<kBlockSize>(dst, value); // Set first block

// Set aligned blocks
size_t offset = kBlockSize - offset_from_last_aligned<kBlockSize>(dst);
for (; offset + kBlockSize < count; offset += kBlockSize)
SetBlock<kBlockSize>(dst + offset, value);

SetLastBlock<kBlockSize>(dst, value, count); // Set last block
}

// A general purpose implementation assuming cheap unaligned writes for sizes:
// 1, 2, 4, 8, 16, 32 and 64 Bytes. Note that some architecture can't store 32
// or 64 Bytes at a time, the compiler will expand them as needed.
Expand Down Expand Up @@ -52,27 +106,26 @@ inline static void GeneralPurposeMemset(char *dst, unsigned char value,
if (count == 0)
return;
if (count == 1)
return SplatSet<scalar::_1>(dst, value);
return SetBlock<1>(dst, value);
if (count == 2)
return SplatSet<scalar::_2>(dst, value);
return SetBlock<2>(dst, value);
if (count == 3)
return SplatSet<scalar::_3>(dst, value);
return SetBlock<3>(dst, value);
if (count == 4)
return SplatSet<scalar::_4>(dst, value);
return SetBlock<4>(dst, value);
if (count <= 8)
return SplatSet<HeadTail<scalar::_4>>(dst, value, count);
return SetBlockOverlap<4>(dst, value, count);
if (count <= 16)
return SplatSet<HeadTail<scalar::_8>>(dst, value, count);
return SetBlockOverlap<8>(dst, value, count);
if (count <= 32)
return SplatSet<HeadTail<scalar::_16>>(dst, value, count);
return SetBlockOverlap<16>(dst, value, count);
if (count <= 64)
return SplatSet<HeadTail<scalar::_32>>(dst, value, count);
return SetBlockOverlap<32>(dst, value, count);
if (count <= 128)
return SplatSet<HeadTail<scalar::_64>>(dst, value, count);
return SplatSet<Align<scalar::_32, Arg::Dst>::Then<Loop<scalar::_32>>>(
dst, value, count);
return SetBlockOverlap<64>(dst, value, count);
return SetAlignedBlocks<32>(dst, value, count);
}

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_STRING_MEMORY_UTILS_MEMSET_UTILS_H
#endif // LIBC_SRC_STRING_MEMORY_UTILS_MEMSET_UTILS_H
35 changes: 14 additions & 21 deletions libc/src/string/x86_64/memcpy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#include "src/string/memcpy.h"
#include "src/__support/common.h"
#include "src/string/memory_utils/elements.h"
#include "src/string/memory_utils/memcpy_utils.h"

namespace __llvm_libc {

Expand All @@ -29,11 +29,8 @@ constexpr size_t kRepMovsBSize =
// Whether target supports AVX instructions.
constexpr bool kHasAvx = LLVM_LIBC_IS_DEFINED(__AVX__);

#ifdef __AVX__
using LoopBlockSize = __llvm_libc::x86::_64;
#else
using LoopBlockSize = __llvm_libc::x86::_32;
#endif
// The chunk size used for the loop copy strategy.
constexpr size_t kLoopCopyBlockSize = kHasAvx ? 64 : 32;

static void CopyRepMovsb(char *__restrict dst, const char *__restrict src,
size_t count) {
Expand Down Expand Up @@ -64,37 +61,33 @@ static void CopyRepMovsb(char *__restrict dst, const char *__restrict src,
// with little change on the code side.
static void memcpy_x86(char *__restrict dst, const char *__restrict src,
size_t count) {
// Use x86 strategies (_1, _2, _3 ...)
using namespace __llvm_libc::x86;

if (kUseOnlyRepMovsb)
return CopyRepMovsb(dst, src, count);

if (count == 0)
return;
if (count == 1)
return Copy<_1>(dst, src);
return CopyBlock<1>(dst, src);
if (count == 2)
return Copy<_2>(dst, src);
return CopyBlock<2>(dst, src);
if (count == 3)
return Copy<_3>(dst, src);
return CopyBlock<3>(dst, src);
if (count == 4)
return Copy<_4>(dst, src);
return CopyBlock<4>(dst, src);
if (count < 8)
return Copy<HeadTail<_4>>(dst, src, count);
return CopyBlockOverlap<4>(dst, src, count);
if (count < 16)
return Copy<HeadTail<_8>>(dst, src, count);
return CopyBlockOverlap<8>(dst, src, count);
if (count < 32)
return Copy<HeadTail<_16>>(dst, src, count);
return CopyBlockOverlap<16>(dst, src, count);
if (count < 64)
return Copy<HeadTail<_32>>(dst, src, count);
return CopyBlockOverlap<32>(dst, src, count);
if (count < 128)
return Copy<HeadTail<_64>>(dst, src, count);
return CopyBlockOverlap<64>(dst, src, count);
if (kHasAvx && count < 256)
return Copy<HeadTail<_128>>(dst, src, count);
return CopyBlockOverlap<128>(dst, src, count);
if (count <= kRepMovsBSize)
return Copy<Align<_32, Arg::Dst>::Then<Loop<LoopBlockSize>>>(dst, src,
count);
return CopyDstAlignedBlocks<kLoopCopyBlockSize, 32>(dst, src, count);
return CopyRepMovsb(dst, src, count);
}

Expand Down
5 changes: 1 addition & 4 deletions libc/test/src/string/memory_utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@ add_libc_unittest(
SUITE
libc_string_unittests
SRCS
elements_test.cpp
memory_access_test.cpp
utils_test.cpp
memcpy_utils_test.cpp
DEPENDS
libc.src.string.memory_utils.memory_utils
libc.utils.CPP.standalone_cpp
COMPILE_OPTIONS
${LIBC_COMPILE_OPTIONS_NATIVE}
)

target_compile_definitions(
Expand Down
103 changes: 0 additions & 103 deletions libc/test/src/string/memory_utils/elements_test.cpp

This file was deleted.

336 changes: 336 additions & 0 deletions libc/test/src/string/memory_utils/memcpy_utils_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
//===-- Unittests for memory_utils ----------------------------------------===//
//
// 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/string/memory_utils/memcpy_utils.h"
#include "utils/CPP/Array.h"
#include "utils/UnitTest/Test.h"

#include <assert.h>
#include <stdint.h> // uintptr_t

#ifndef LLVM_LIBC_MEMCPY_MONITOR
#error LLVM_LIBC_MEMCPY_MONITOR must be defined for this test.
#endif

namespace __llvm_libc {

struct Buffer {
static constexpr size_t kMaxBuffer = 1024;
char buffer[kMaxBuffer + 1];
size_t last = 0;

void Clear() {
last = 0;
for (size_t i = 0; i < kMaxBuffer; ++i)
buffer[i] = '0';
buffer[kMaxBuffer] = '\0';
}

void Increment(const void *ptr) {
const auto offset = reinterpret_cast<uintptr_t>(ptr);
assert(offset < kMaxBuffer);
++buffer[offset];
if (offset > last)
last = offset;
}

char *Finish() {
assert(last < kMaxBuffer);
buffer[last + 1] = '\0';
return buffer;
}
};

struct Trace {
Buffer read;
Buffer write;

void Add(char *__restrict dst, const char *__restrict src, size_t count) {
for (size_t i = 0; i < count; ++i)
read.Increment(src + i);
for (size_t i = 0; i < count; ++i)
write.Increment(dst + i);
}

void Clear() {
read.Clear();
write.Clear();
}

char *Read() { return read.Finish(); }
char *Write() { return write.Finish(); }
};

static Trace &GetTrace() {
static thread_local Trace events;
return events;
}

extern "C" void LLVM_LIBC_MEMCPY_MONITOR(char *__restrict dst,
const char *__restrict src,
size_t count) {
GetTrace().Add(dst, src, count);
}

char *I(uintptr_t offset) { return reinterpret_cast<char *>(offset); }

TEST(LlvmLibcMemcpyUtilsTest, CopyTrivial) {
auto &trace = GetTrace();

trace.Clear();
CopyBlock<1>(I(0), I(0));
EXPECT_STREQ(trace.Write(), "1");
EXPECT_STREQ(trace.Read(), "1");

trace.Clear();
CopyBlock<2>(I(0), I(0));
EXPECT_STREQ(trace.Write(), "11");
EXPECT_STREQ(trace.Read(), "11");

trace.Clear();
CopyBlock<4>(I(0), I(0));
EXPECT_STREQ(trace.Write(), "1111");
EXPECT_STREQ(trace.Read(), "1111");

trace.Clear();
CopyBlock<8>(I(0), I(0));
EXPECT_STREQ(trace.Write(), "11111111");
EXPECT_STREQ(trace.Read(), "11111111");

trace.Clear();
CopyBlock<16>(I(0), I(0));
EXPECT_STREQ(trace.Write(), "1111111111111111");
EXPECT_STREQ(trace.Read(), "1111111111111111");

trace.Clear();
CopyBlock<32>(I(0), I(0));
EXPECT_STREQ(trace.Write(), "11111111111111111111111111111111");
EXPECT_STREQ(trace.Read(), "11111111111111111111111111111111");

trace.Clear();
CopyBlock<64>(I(0), I(0));
EXPECT_STREQ(
trace.Write(),
"1111111111111111111111111111111111111111111111111111111111111111");
EXPECT_STREQ(
trace.Read(),
"1111111111111111111111111111111111111111111111111111111111111111");
}

TEST(LlvmLibcMemcpyUtilsTest, CopyOffset) {
auto &trace = GetTrace();

trace.Clear();
CopyBlock<1>(I(3), I(1));
EXPECT_STREQ(trace.Write(), "0001");
EXPECT_STREQ(trace.Read(), "01");

trace.Clear();
CopyBlock<1>(I(2), I(1));
EXPECT_STREQ(trace.Write(), "001");
EXPECT_STREQ(trace.Read(), "01");
}

TEST(LlvmLibcMemcpyUtilsTest, CopyBlockOverlap) {
auto &trace = GetTrace();

trace.Clear();
CopyBlockOverlap<2>(I(0), I(0), 2);
EXPECT_STREQ(trace.Write(), "22");
EXPECT_STREQ(trace.Read(), "22");

trace.Clear();
CopyBlockOverlap<2>(I(0), I(0), 3);
EXPECT_STREQ(trace.Write(), "121");
EXPECT_STREQ(trace.Read(), "121");

trace.Clear();
CopyBlockOverlap<2>(I(0), I(0), 4);
EXPECT_STREQ(trace.Write(), "1111");
EXPECT_STREQ(trace.Read(), "1111");

trace.Clear();
CopyBlockOverlap<4>(I(2), I(1), 7);
EXPECT_STREQ(trace.Write(), "001112111");
EXPECT_STREQ(trace.Read(), "01112111");
}

TEST(LlvmLibcMemcpyUtilsTest, CopySrcAlignedBlocks) {
auto &trace = GetTrace();
// Source is aligned and multiple of alignment.
// "1111"
trace.Clear();
CopySrcAlignedBlocks<4>(I(0), I(0), 4);
EXPECT_STREQ(trace.Write(), "2222");
EXPECT_STREQ(trace.Read(), "2222");

// Source is aligned and multiple of alignment.
// "11110000"
// + "00001111"
// = "11111111"
trace.Clear();
CopySrcAlignedBlocks<4>(I(0), I(0), 8);
EXPECT_STREQ(trace.Write(), "11111111");
EXPECT_STREQ(trace.Read(), "11111111");

// Source is aligned already overlap at end.
// "1111000000000"
// + "0000111100000"
// + "0000000011110"
// + "0000000001111"
// = "1111111112221"
trace.Clear();
CopySrcAlignedBlocks<4>(I(0), I(0), 13);
EXPECT_STREQ(trace.Write(), "1111111112221");
EXPECT_STREQ(trace.Read(), "1111111112221");

// Misaligned source.
// "01111000000000"
// + "00001111000000"
// + "00000000111100"
// + "00000000001111"
// = "01112111112211"
trace.Clear();
CopySrcAlignedBlocks<4>(I(0), I(1), 13);
EXPECT_STREQ(trace.Write(), "1112111112211");
EXPECT_STREQ(trace.Read(), "01112111112211");

// Misaligned source aligned at end.
// "011110000000"
// + "000011110000"
// + "000000001111"
// = "011121111111"
trace.Clear();
CopySrcAlignedBlocks<4>(I(0), I(1), 11);
EXPECT_STREQ(trace.Write(), "11121111111");
EXPECT_STREQ(trace.Read(), "011121111111");
}

TEST(LlvmLibcMemcpyUtilsTest, CopyDstAlignedBlocks) {
auto &trace = GetTrace();
// Destination is aligned and multiple of alignment.
// "1111"
trace.Clear();
CopyDstAlignedBlocks<4>(I(0), I(0), 4);
EXPECT_STREQ(trace.Write(), "2222");
EXPECT_STREQ(trace.Read(), "2222");

// Destination is aligned and multiple of alignment.
// "11110000"
// + "00001111"
// = "11111111"
trace.Clear();
CopyDstAlignedBlocks<4>(I(0), I(0), 8);
EXPECT_STREQ(trace.Write(), "11111111");
EXPECT_STREQ(trace.Read(), "11111111");

// Destination is aligned already overlap at end.
// "1111000000000"
// + "0000111100000"
// + "0000000011110"
// + "0000000001111"
// = "1111111112221"
trace.Clear();
CopyDstAlignedBlocks<4>(I(0), I(0), 13);
EXPECT_STREQ(trace.Write(), "1111111112221");
EXPECT_STREQ(trace.Read(), "1111111112221");

// Misaligned destination.
// "01111000000000"
// + "00001111000000"
// + "00000000111100"
// + "00000000001111"
// = "01112111112211"
trace.Clear();
CopyDstAlignedBlocks<4>(I(1), I(0), 13);
EXPECT_STREQ(trace.Write(), "01112111112211");
EXPECT_STREQ(trace.Read(), "1112111112211");

// Misaligned destination aligned at end.
// "011110000000"
// + "000011110000"
// + "000000001111"
// = "011121111111"
trace.Clear();
CopyDstAlignedBlocks<4>(I(1), I(0), 11);
EXPECT_STREQ(trace.Write(), "011121111111");
EXPECT_STREQ(trace.Read(), "11121111111");
}

TEST(LlvmLibcMemcpyUtilsTest, CopyAlignedBlocksWithAlignment) {
auto &trace = GetTrace();
// Source is aligned and multiple of alignment.
// "11111111"
trace.Clear();
CopySrcAlignedBlocks<8, 4>(I(0), I(0), 8);
EXPECT_STREQ(trace.Write(), "22221111");
EXPECT_STREQ(trace.Read(), "22221111");

// Destination is aligned and multiple of alignment.
// "11111111"
trace.Clear();
CopyDstAlignedBlocks<8, 4>(I(0), I(0), 8);
EXPECT_STREQ(trace.Write(), "22221111");
EXPECT_STREQ(trace.Read(), "22221111");

// Source is aligned and multiple of alignment.
// "111111111"
trace.Clear();
CopySrcAlignedBlocks<8, 4>(I(0), I(0), 9);
EXPECT_STREQ(trace.Write(), "122211111");
EXPECT_STREQ(trace.Read(), "122211111");

// Destination is aligned and multiple of alignment.
// "111111111"
trace.Clear();
CopyDstAlignedBlocks<8, 4>(I(0), I(0), 9);
EXPECT_STREQ(trace.Write(), "122211111");
EXPECT_STREQ(trace.Read(), "122211111");
}

TEST(LlvmLibcMemcpyUtilsTest, CopyAlignedBlocksMaxReloads) {
auto &trace = GetTrace();
for (size_t alignment = 0; alignment < 32; ++alignment) {
for (size_t count = 64; count < 768; ++count) {
trace.Clear();
// We should never reload more than twice when copying from count = 2x32.
CopySrcAlignedBlocks<32>(I(alignment), I(0), count);
const char *const written = trace.Write();
// First bytes are untouched.
for (size_t i = 0; i < alignment; ++i)
EXPECT_EQ(written[i], '0');
// Next bytes are loaded once or twice but no more.
for (size_t i = alignment; i < count; ++i) {
EXPECT_GE(written[i], '1');
EXPECT_LE(written[i], '2');
}
}
}
}

TEST(LlvmLibcMemcpyUtilsTest, CopyAlignedBlocksWithAlignmentMaxReloads) {
auto &trace = GetTrace();
for (size_t alignment = 0; alignment < 32; ++alignment) {
for (size_t count = 64; count < 768; ++count) {
trace.Clear();
// We should never reload more than twice when copying from count = 2x32.
CopySrcAlignedBlocks<32, 16>(I(alignment), I(0), count);
const char *const written = trace.Write();
// First bytes are untouched.
for (size_t i = 0; i < alignment; ++i)
EXPECT_EQ(written[i], '0');
// Next bytes are loaded once or twice but no more.
for (size_t i = alignment; i < count; ++i) {
EXPECT_GE(written[i], '1');
EXPECT_LE(written[i], '2');
}
}
}
}

} // namespace __llvm_libc
231 changes: 0 additions & 231 deletions libc/test/src/string/memory_utils/memory_access_test.cpp

This file was deleted.