10 changes: 10 additions & 0 deletions libc/src/string/x86/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,13 @@ add_memcpy("memcpy_${LIBC_TARGET_MACHINE}_opt_none" REJECT "${ALL_CPU_FEATURES}"
add_memcpy("memcpy_${LIBC_TARGET_MACHINE}_opt_sse" REQUIRE "SSE" REJECT "SSE2")
add_memcpy("memcpy_${LIBC_TARGET_MACHINE}_opt_avx" REQUIRE "AVX" REJECT "AVX2")
add_memcpy("memcpy_${LIBC_TARGET_MACHINE}_opt_avx512f" REQUIRE "AVX512F")

add_memset("memset_${LIBC_TARGET_MACHINE}_opt_none" REJECT "${ALL_CPU_FEATURES}")
add_memset("memset_${LIBC_TARGET_MACHINE}_opt_sse" REQUIRE "SSE" REJECT "SSE2")
add_memset("memset_${LIBC_TARGET_MACHINE}_opt_avx" REQUIRE "AVX" REJECT "AVX2")
add_memset("memset_${LIBC_TARGET_MACHINE}_opt_avx512f" REQUIRE "AVX512F")

add_bzero("bzero_${LIBC_TARGET_MACHINE}_opt_none" REJECT "${ALL_CPU_FEATURES}")
add_bzero("bzero_${LIBC_TARGET_MACHINE}_opt_sse" REQUIRE "SSE" REJECT "SSE2")
add_bzero("bzero_${LIBC_TARGET_MACHINE}_opt_avx" REQUIRE "AVX" REJECT "AVX2")
add_bzero("bzero_${LIBC_TARGET_MACHINE}_opt_avx512f" REQUIRE "AVX512F")
43 changes: 24 additions & 19 deletions libc/test/src/string/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,28 @@ add_libc_unittest(
libc.src.string.strlen
)

# Tests all implementations of memcpy that can run on the host.
get_property(memcpy_implementations GLOBAL PROPERTY memcpy_implementations)
foreach(memcpy_config_name IN LISTS memcpy_implementations)
get_target_property(require_cpu_features libc.src.string.${memcpy_config_name} REQUIRE_CPU_FEATURES)
host_supports(can_run "${require_cpu_features}")
if(can_run)
add_libc_unittest(
${memcpy_config_name}_test
SUITE
libc_string_unittests
SRCS
memcpy_test.cpp
DEPENDS
libc.src.string.${memcpy_config_name}
)
else()
message(STATUS "Skipping test for '${memcpy_config_name}' insufficient host cpu features")
endif()
endforeach()
# Tests all implementations that can run on the host.
function(add_libc_multi_impl_test name)
get_property(fq_implementations GLOBAL PROPERTY ${name}_implementations)
foreach(fq_config_name IN LISTS fq_implementations)
get_target_property(required_cpu_features ${fq_config_name} REQUIRE_CPU_FEATURES)
host_supports(can_run "${required_cpu_features}")
if(can_run)
add_libc_unittest(
${fq_config_name}_test
SUITE
libc_string_unittests
DEPENDS
${fq_config_name}
${ARGN}
)
else()
message(STATUS "Skipping test for '${fq_config_name}' insufficient host cpu features '${required_cpu_features}'")
endif()
endforeach()
endfunction()

add_libc_multi_impl_test(memcpy SRCS memcpy_test.cpp)
add_libc_multi_impl_test(memset SRCS memset_test.cpp)
add_libc_multi_impl_test(bzero SRCS bzero_test.cpp)

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

using __llvm_libc::cpp::Array;
using __llvm_libc::cpp::ArrayRef;
using Data = Array<char, 2048>;

static const ArrayRef<char> kDeadcode("DEADC0DE", 8);

// Returns a Data object filled with a repetition of `filler`.
Data getData(ArrayRef<char> filler) {
Data out;
for (size_t i = 0; i < out.size(); ++i)
out[i] = filler[i % filler.size()];
return out;
}

TEST(BzeroTest, Thorough) {
const Data dirty = getData(kDeadcode);
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]);
}
}
}

// 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.
11 changes: 6 additions & 5 deletions libc/test/src/string/memcpy_test.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
//===----------------------- Unittests for memcpy -------------------------===//
//===-- Unittests for memcpy ----------------------------------------------===//
//
// 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/memcpy.h"
#include "utils/CPP/ArrayRef.h"
#include "utils/UnitTest/Test.h"
#include "src/string/memcpy.h"

using __llvm_libc::cpp::Array;
using __llvm_libc::cpp::ArrayRef;
using __llvm_libc::cpp::MutableArrayRef;
using Data = Array<char, 2048>;

static const ArrayRef<char> kNumbers("0123456789", 10);
Expand All @@ -33,8 +32,10 @@ TEST(MemcpyTest, Thorough) {
for (size_t align = 0; align < 64; ++align) {
auto buffer = dirty;
const char *const src = groundtruth.data();
char *const dst = &buffer[align];
__llvm_libc::memcpy(dst, src, count);
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]);
Expand Down
53 changes: 53 additions & 0 deletions libc/test/src/string/memset_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//===-- Unittests for memset ----------------------------------------------===//
//
// 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/memset.h"
#include "utils/CPP/ArrayRef.h"
#include "utils/UnitTest/Test.h"

using __llvm_libc::cpp::Array;
using __llvm_libc::cpp::ArrayRef;
using Data = Array<char, 2048>;

static const ArrayRef<char> kDeadcode("DEADC0DE", 8);

// Returns a Data object filled with a repetition of `filler`.
Data getData(ArrayRef<char> filler) {
Data out;
for (size_t i = 0; i < out.size(); ++i)
out[i] = filler[i % filler.size()];
return out;
}

TEST(MemsetTest, Thorough) {
const Data dirty = getData(kDeadcode);
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]);
}
}
}
}

// 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.