diff --git a/libc/src/string/memmove.cpp b/libc/src/string/memmove.cpp --- a/libc/src/string/memmove.cpp +++ b/libc/src/string/memmove.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "src/string/memmove.h" +#include "src/string/memory_utils/memcpy_implementations.h" #include "src/string/memory_utils/memmove_implementations.h" #include // size_t @@ -14,7 +15,10 @@ LLVM_LIBC_FUNCTION(void *, memmove, (void *dst, const void *src, size_t count)) { - inline_memmove(dst, src, count); + if (disjoint(dst, src, count)) + inline_memcpy(dst, src, count); + else + inline_memmove(dst, src, count); return dst; } diff --git a/libc/src/string/memory_utils/utils.h b/libc/src/string/memory_utils/utils.h --- a/libc/src/string/memory_utils/utils.h +++ b/libc/src/string/memory_utils/utils.h @@ -84,6 +84,21 @@ return reinterpret_cast(__builtin_assume_aligned(ptr, alignment)); } +// Returns true iff memory regions [p1, p1 + size] and [p2, 2 + size] are +// disjoint. +LIBC_INLINE bool disjoint(const void *p1, const void *p2, size_t size) { + const char *a = static_cast(p1); + const char *b = static_cast(p2); + if (a > b) { + // Swap a and b, this is compile down to conditionnal move for aarch64 / x86 + // and RISCV with zbb extension. + auto tmp = a; + a = b; + b = tmp; + } + return a + size <= b; +} + #if LIBC_HAS_BUILTIN(__builtin_memcpy_inline) #define LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE #endif diff --git a/libc/test/src/string/memory_utils/utils_test.cpp b/libc/test/src/string/memory_utils/utils_test.cpp --- a/libc/test/src/string/memory_utils/utils_test.cpp +++ b/libc/test/src/string/memory_utils/utils_test.cpp @@ -144,6 +144,17 @@ } } +TEST(LlvmLibcUtilsTest, DisjointBuffers) { + char a, b; + EXPECT_TRUE(disjoint(&a, &b, 0)); + EXPECT_TRUE(disjoint(&a, &b, 1)); + EXPECT_FALSE(disjoint(&a, &b, 2)); + + EXPECT_TRUE(disjoint(&b, &a, 0)); + EXPECT_TRUE(disjoint(&b, &a, 1)); + EXPECT_FALSE(disjoint(&b, &a, 2)); +} + TEST(LlvmLibcUtilsTest, LoadStoreAligned) { const uint64_t init = 0xDEAD'C0DE'BEEF'F00D; CPtr const src = reinterpret_cast(&init);