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 <stddef.h> // 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 (is_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<T *>(__builtin_assume_aligned(ptr, alignment));
 }
 
+// Returns true iff memory regions [p1, p1 + size] and [p2, p2 + size] are
+// disjoint.
+LIBC_INLINE bool is_disjoint(const void *p1, const void *p2, size_t size) {
+  const char *a = static_cast<const char *>(p1);
+  const char *b = static_cast<const char *>(p2);
+  if (a > b) {
+    // Swap a and b, this compiles down to conditionnal move for aarch64, x86
+    // and RISCV with zbb extension.
+    const char *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,19 @@
   }
 }
 
+TEST(LlvmLibcUtilsTest, DisjointBuffers) {
+  char buf[3];
+  const char *const a = buf + 0;
+  const char *const b = buf + 1;
+  EXPECT_TRUE(is_disjoint(a, b, 0));
+  EXPECT_TRUE(is_disjoint(a, b, 1));
+  EXPECT_FALSE(is_disjoint(a, b, 2));
+
+  EXPECT_TRUE(is_disjoint(b, a, 0));
+  EXPECT_TRUE(is_disjoint(b, a, 1));
+  EXPECT_FALSE(is_disjoint(b, a, 2));
+}
+
 TEST(LlvmLibcUtilsTest, LoadStoreAligned) {
   const uint64_t init = 0xDEAD'C0DE'BEEF'F00D;
   CPtr const src = reinterpret_cast<CPtr>(&init);