diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt --- a/libc/config/darwin/arm/entrypoints.txt +++ b/libc/config/darwin/arm/entrypoints.txt @@ -19,6 +19,7 @@ # string.h entrypoints libc.src.string.bcmp + libc.src.string.bcopy libc.src.string.bzero libc.src.string.memccpy libc.src.string.memchr diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -28,6 +28,7 @@ # string.h entrypoints libc.src.string.bcmp + libc.src.string.bcopy libc.src.string.bzero libc.src.string.memccpy libc.src.string.memchr diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -19,6 +19,7 @@ # string.h entrypoints libc.src.string.bcmp + libc.src.string.bcopy libc.src.string.bzero libc.src.string.memccpy libc.src.string.memchr diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -28,6 +28,7 @@ # string.h entrypoints libc.src.string.bcmp + libc.src.string.bcopy libc.src.string.bzero libc.src.string.memccpy libc.src.string.memchr diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -19,6 +19,7 @@ # string.h entrypoints libc.src.string.bcmp + libc.src.string.bcopy libc.src.string.bzero libc.src.string.memccpy libc.src.string.memchr diff --git a/libc/docs/strings.rst b/libc/docs/strings.rst --- a/libc/docs/strings.rst +++ b/libc/docs/strings.rst @@ -36,6 +36,7 @@ ============= ========= bzero |check| bcmp |check| +bcopy |check| memcpy |check| memset |check| memcmp |check| diff --git a/libc/spec/llvm_libc_ext.td b/libc/spec/llvm_libc_ext.td --- a/libc/spec/llvm_libc_ext.td +++ b/libc/spec/llvm_libc_ext.td @@ -5,6 +5,11 @@ [], // Types [], // Enumerations [ + FunctionSpec< + "bcopy", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, FunctionSpec< "bzero", RetValSpec, diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -20,6 +20,14 @@ .memory_utils.memcpy_implementation ) +add_entrypoint_object( + bcopy + SRCS + bcopy.cpp + HDRS + bcopy.h +) + add_entrypoint_object( memccpy SRCS @@ -28,7 +36,6 @@ memccpy.h ) - add_entrypoint_object( mempcpy SRCS diff --git a/libc/src/string/bcopy.h b/libc/src/string/bcopy.h new file mode 100644 --- /dev/null +++ b/libc/src/string/bcopy.h @@ -0,0 +1,20 @@ +//===-- Implementation header for bcopy -------------------------*- 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 LLVM_LIBC_SRC_STRING_BCOPY_H +#define LLVM_LIBC_SRC_STRING_BCOPY_H + +#include // size_t + +namespace __llvm_libc { + +void bcopy(const void *src, void *dest, size_t count); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_BCOPY_H diff --git a/libc/src/string/bcopy.cpp b/libc/src/string/bcopy.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/bcopy.cpp @@ -0,0 +1,19 @@ +//===-- Implementation of bcopy -------------------------------------------===// +// +// 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/bcopy.h" +#include "src/__support/common.h" +#include "src/string/memory_utils/memmove_implementations.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(void, bcopy, (const void *src, void *dst, size_t count)) { + return inline_memmove(dst, src, count); +} + +} // namespace __llvm_libc diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -2,6 +2,18 @@ add_subdirectory(memory_utils) +add_libc_unittest( + bcopy_test + SUITE + libc_string_unittests + SRCS + bcopy_test.cpp + DEPENDS + libc.src.string.bcopy + LINK_LIBRARIES + LibcMemoryHelpers +) + add_libc_unittest( memccpy_test SUITE diff --git a/libc/test/src/string/bcopy_test.cpp b/libc/test/src/string/bcopy_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/bcopy_test.cpp @@ -0,0 +1,104 @@ +//===-- Unittests for bcopy -----------------------------------------------===// +// +// 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/span.h" +#include "src/string/bcopy.h" +#include "utils/UnitTest/MemoryMatcher.h" +#include "utils/UnitTest/Test.h" + +using __llvm_libc::cpp::array; +using __llvm_libc::cpp::span; + +TEST(LlvmLibcBcopyTest, MoveZeroByte) { + char Buffer[] = {'a', 'b', 'y', 'z'}; + const char Expected[] = {'a', 'b', 'y', 'z'}; + void *const Dst = Buffer; + __llvm_libc::bcopy(Buffer + 2, Dst, 0); + ASSERT_MEM_EQ(Buffer, Expected); +} + +TEST(LlvmLibcBcopyTest, DstAndSrcPointToSameAddress) { + char Buffer[] = {'a', 'b'}; + const char Expected[] = {'a', 'b'}; + void *const Dst = Buffer; + __llvm_libc::bcopy(Buffer, Dst, 1); + ASSERT_MEM_EQ(Buffer, Expected); +} + +TEST(LlvmLibcBcopyTest, DstStartsBeforeSrc) { + // Set boundary at beginning and end for not overstepping when + // copy forward or backward. + char Buffer[] = {'z', 'a', 'b', 'c', 'z'}; + const char Expected[] = {'z', 'b', 'c', 'c', 'z'}; + void *const Dst = Buffer + 1; + __llvm_libc::bcopy(Buffer + 2, Dst, 2); + ASSERT_MEM_EQ(Buffer, Expected); +} + +TEST(LlvmLibcBcopyTest, DstStartsAfterSrc) { + char Buffer[] = {'z', 'a', 'b', 'c', 'z'}; + const char Expected[] = {'z', 'a', 'a', 'b', 'z'}; + void *const Dst = Buffer + 2; + __llvm_libc::bcopy(Buffer + 1, Dst, 2); + ASSERT_MEM_EQ(Buffer, Expected); +} + +// e.g. `Dst` follow `src`. +// str: [abcdefghij] +// [__src_____] +// [_____Dst__] +TEST(LlvmLibcBcopyTest, SrcFollowDst) { + char Buffer[] = {'z', 'a', 'b', 'z'}; + const char Expected[] = {'z', 'b', 'b', 'z'}; + void *const Dst = Buffer + 1; + __llvm_libc::bcopy(Buffer + 2, Dst, 1); + ASSERT_MEM_EQ(Buffer, Expected); +} + +TEST(LlvmLibcBcopyTest, DstFollowSrc) { + char Buffer[] = {'z', 'a', 'b', 'z'}; + const char Expected[] = {'z', 'a', 'a', 'z'}; + void *const Dst = Buffer + 2; + __llvm_libc::bcopy(Buffer + 1, Dst, 1); + ASSERT_MEM_EQ(Buffer, Expected); +} + +static constexpr int kMaxSize = 512; + +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; +} + +void Randomize(span Buffer) { + for (auto ¤t : Buffer) + current = GetRandomChar(); +} + +TEST(LlvmLibcBcopyTest, SizeSweep) { + using LargeBuffer = array; + LargeBuffer GroundTruth; + Randomize(GroundTruth); + for (int Size = 0; Size < kMaxSize; ++Size) { + for (int Offset = -Size; Offset < Size; ++Offset) { + LargeBuffer Buffer = GroundTruth; + LargeBuffer Expected = GroundTruth; + size_t DstOffset = kMaxSize; + size_t SrcOffset = kMaxSize + Offset; + for (int I = 0; I < Size; ++I) + Expected[DstOffset + I] = GroundTruth[SrcOffset + I]; + void *const Dst = Buffer.data() + DstOffset; + __llvm_libc::bcopy(Buffer.data() + SrcOffset, Dst, Size); + ASSERT_MEM_EQ(Buffer, Expected); + } + } +} diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -1124,6 +1124,17 @@ ], ) +libc_function( + name = "bcopy", + srcs = ["src/string/bcopy.cpp"], + hdrs = ["src/string/bcopy.h"], + features = no_sanitize_features, + deps = [ + ":__support_common", + ":string_memory_utils", + ], +) + libc_function( name = "memcmp", srcs = ["src/string/memcmp.cpp"], diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/string/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/string/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/libc/test/src/string/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/test/src/string/BUILD.bazel @@ -146,6 +146,18 @@ ], ) +libc_test( + name = "bcopy_test", + srcs = ["bcopy_test.cpp"], + libc_function_deps = [ + "//libc:bcopy", + ], + deps = [ + "//libc:__support_cpp_span", + "//libc/utils/UnitTest:memory_matcher", + ], +) + libc_test( name = "memcmp_test", srcs = ["memcmp_test.cpp"],