diff --git a/libc/benchmarks/CMakeLists.txt b/libc/benchmarks/CMakeLists.txt --- a/libc/benchmarks/CMakeLists.txt +++ b/libc/benchmarks/CMakeLists.txt @@ -171,6 +171,7 @@ add_libc_multi_impl_benchmark(memset) add_libc_multi_impl_benchmark(bzero) add_libc_multi_impl_benchmark(memcmp) +add_libc_multi_impl_benchmark(bcmp) #============================================================================== # Google Benchmarking tool @@ -188,6 +189,7 @@ PRIVATE libc-memory-benchmark libc.src.string.memcmp_opt_host + libc.src.string.bcmp_opt_host libc.src.string.memcpy_opt_host libc.src.string.memset_opt_host libc.src.string.bzero_opt_host diff --git a/libc/benchmarks/LibcMemoryBenchmarkMain.cpp b/libc/benchmarks/LibcMemoryBenchmarkMain.cpp --- a/libc/benchmarks/LibcMemoryBenchmarkMain.cpp +++ b/libc/benchmarks/LibcMemoryBenchmarkMain.cpp @@ -27,6 +27,7 @@ extern void *memset(void *, int, size_t); extern void bzero(void *, size_t); extern int memcmp(const void *, const void *, size_t); +extern int bcmp(const void *, const void *, size_t); } // namespace __llvm_libc @@ -76,6 +77,9 @@ #elif defined(LIBC_BENCHMARK_FUNCTION_MEMCMP) #define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_MEMCMP using BenchmarkSetup = ComparisonSetup; +#elif defined(LIBC_BENCHMARK_FUNCTION_BCMP) +#define LIBC_BENCHMARK_FUNCTION LIBC_BENCHMARK_FUNCTION_BCMP +using BenchmarkSetup = ComparisonSetup; #else #error "Missing LIBC_BENCHMARK_FUNCTION_XXX definition" #endif diff --git a/libc/benchmarks/LibcMemoryGoogleBenchmarkMain.cpp b/libc/benchmarks/LibcMemoryGoogleBenchmarkMain.cpp --- a/libc/benchmarks/LibcMemoryGoogleBenchmarkMain.cpp +++ b/libc/benchmarks/LibcMemoryGoogleBenchmarkMain.cpp @@ -28,6 +28,7 @@ extern void *memset(void *, int, size_t); extern void bzero(void *, size_t); extern int memcmp(const void *, const void *, size_t); +extern int bcmp(const void *, const void *, size_t); } // namespace __llvm_libc @@ -38,6 +39,9 @@ static constexpr MemcmpConfiguration kMemcmpConfigurations[] = { {__llvm_libc::memcmp, "__llvm_libc::memcmp"}}; +static constexpr MemcmpConfiguration kBcmpConfigurations[] = { + {__llvm_libc::bcmp, "__llvm_libc::bcmp"}}; + static constexpr MemsetConfiguration kMemsetConfigurations[] = { {__llvm_libc::memset, "__llvm_libc::memset"}}; @@ -116,6 +120,8 @@ llvm::makeArrayRef(kMemcpyConfigurations)); BENCHMARK_MEMORY_FUNCTION(BM_Memcmp, ComparisonSetup, MemcmpConfiguration, llvm::makeArrayRef(kMemcmpConfigurations)); +BENCHMARK_MEMORY_FUNCTION(BM_Bcmp, ComparisonSetup, MemcmpConfiguration, + llvm::makeArrayRef(kBcmpConfigurations)); BENCHMARK_MEMORY_FUNCTION(BM_Memset, SetSetup, MemsetConfiguration, llvm::makeArrayRef(kMemsetConfigurations)); BENCHMARK_MEMORY_FUNCTION(BM_Bzero, SetSetup, BzeroConfiguration, 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 @@ -21,6 +21,7 @@ libc.src.errno.__errno_location # string.h entrypoints + libc.src.string.bcmp libc.src.string.bzero libc.src.string.memchr libc.src.string.memcmp 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 @@ -21,6 +21,7 @@ libc.src.errno.__errno_location # string.h entrypoints + libc.src.string.bcmp libc.src.string.bzero libc.src.string.memchr libc.src.string.memcmp 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 @@ -331,3 +331,35 @@ add_memcmp(memcmp_opt_host COMPILE_OPTIONS ${LIBC_COMPILE_OPTIONS_NATIVE}) add_memcmp(memcmp) endif() + +# ------------------------------------------------------------------------------ +# bcmp +# ------------------------------------------------------------------------------ + +function(add_bcmp bcmp_name) + add_implementation(bcmp ${bcmp_name} + SRCS ${LIBC_BCMP_SRC} + HDRS ${LIBC_SOURCE_DIR}/src/string/bcmp.h + DEPENDS + .memory_utils.memory_utils + libc.include.string + COMPILE_OPTIONS + -fno-builtin-memcmp + -fno-builtin-bcmp + ${ARGN} + ) +endfunction() + +if(${LIBC_TARGET_ARCHITECTURE_IS_X86}) + set(LIBC_BCMP_SRC ${LIBC_SOURCE_DIR}/src/string/bcmp.cpp) + add_bcmp(bcmp_x86_64_opt_sse2 COMPILE_OPTIONS -march=k8 REQUIRE SSE2) + add_bcmp(bcmp_x86_64_opt_sse4 COMPILE_OPTIONS -march=nehalem REQUIRE SSE4_2) + add_bcmp(bcmp_x86_64_opt_avx2 COMPILE_OPTIONS -march=haswell REQUIRE AVX2) + add_bcmp(bcmp_x86_64_opt_avx512 COMPILE_OPTIONS -march=skylake-avx512 REQUIRE AVX512F) + add_bcmp(bcmp_opt_host COMPILE_OPTIONS ${LIBC_COMPILE_OPTIONS_NATIVE}) + add_bcmp(bcmp) +else() + set(LIBC_BCMP_SRC ${LIBC_SOURCE_DIR}/src/string/bcmp.cpp) + add_bcmp(bcmp_opt_host COMPILE_OPTIONS ${LIBC_COMPILE_OPTIONS_NATIVE}) + add_bcmp(bcmp) +endif() diff --git a/libc/src/string/bcmp.h b/libc/src/string/bcmp.h new file mode 100644 --- /dev/null +++ b/libc/src/string/bcmp.h @@ -0,0 +1,20 @@ +//===-- Implementation header for bzero -------------------------*- 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_BCMP_H +#define LLVM_LIBC_SRC_STRING_BCMP_H + +#include // size_t + +namespace __llvm_libc { + +int bcmp(const void *lhs, const void *rhs, size_t count); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_BCMP_H diff --git a/libc/src/string/bcmp.cpp b/libc/src/string/bcmp.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/bcmp.cpp @@ -0,0 +1,25 @@ +//===-- Implementation of bcmp --------------------------------------------===// +// +// 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/bcmp.h" +#include "src/__support/common.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, bcmp, + (const void *lhs, const void *rhs, size_t count)) { + const unsigned char *_lhs = reinterpret_cast(lhs); + const unsigned char *_rhs = reinterpret_cast(rhs); + for (size_t i = 0; i < count; ++i) + if (_lhs[i] != _rhs[i]) + return 1; + // count is 0 or _lhs and _rhs are the same. + return 0; +} + +} // 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 @@ -199,4 +199,5 @@ add_libc_multi_impl_test(memset SRCS memset_test.cpp) add_libc_multi_impl_test(bzero SRCS bzero_test.cpp) add_libc_multi_impl_test(memcmp SRCS memcmp_test.cpp) +add_libc_multi_impl_test(bcmp SRCS bcmp_test.cpp) add_libc_multi_impl_test(memmove SRCS memmove_test.cpp) diff --git a/libc/test/src/string/bcmp_test.cpp b/libc/test/src/string/bcmp_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/bcmp_test.cpp @@ -0,0 +1,58 @@ +//===-- Unittests for bcmp ------------------------------------------------===// +// +// 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/bcmp.h" +#include "utils/UnitTest/Test.h" + +TEST(LlvmLibcBcmpTest, CmpZeroByte) { + const char *lhs = "ab"; + const char *rhs = "bc"; + EXPECT_EQ(__llvm_libc::bcmp(lhs, rhs, 0), 0); +} + +TEST(LlvmLibcBcmpTest, LhsRhsAreTheSame) { + const char *lhs = "ab"; + const char *rhs = "ab"; + EXPECT_EQ(__llvm_libc::bcmp(lhs, rhs, 2), 0); +} + +TEST(LlvmLibcBcmpTest, LhsBeforeRhsLexically) { + const char *lhs = "ab"; + const char *rhs = "ac"; + EXPECT_EQ(__llvm_libc::bcmp(lhs, rhs, 2), 1); +} + +TEST(LlvmLibcBcmpTest, LhsAfterRhsLexically) { + const char *lhs = "ac"; + const char *rhs = "ab"; + EXPECT_EQ(__llvm_libc::bcmp(lhs, rhs, 2), 1); +} + +TEST(LlvmLibcBcmpTest, Sweep) { + static constexpr size_t kMaxSize = 1024; + char lhs[kMaxSize]; + char rhs[kMaxSize]; + + const auto reset = [](char *const ptr) { + for (size_t i = 0; i < kMaxSize; ++i) + ptr[i] = 'a'; + }; + + reset(lhs); + reset(rhs); + for (size_t i = 0; i < kMaxSize; ++i) + EXPECT_EQ(__llvm_libc::bcmp(lhs, rhs, i), 0); + + reset(lhs); + reset(rhs); + for (size_t i = 0; i < kMaxSize; ++i) { + rhs[i] = 'b'; + EXPECT_EQ(__llvm_libc::bcmp(lhs, rhs, kMaxSize), 1); + rhs[i] = 'a'; + } +}