[libc-commits] [libc] [libc][MVE] implement MVE accelerated memcmp (PR #224857)

via libc-commits libc-commits at lists.llvm.org
Sat Sep 19 13:31:01 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Schrodinger ZHU Yifan (SchrodingerZhu)

<details>
<summary>Changes</summary>

This PR adds an MVE accelerated memcmp implementation for arm targets. The original implementation on arm only uses a byte-per-iteration implementation.

On RA8M2 (1Ghz) with TCM and D-Cache, DWT cycle counts:

| Distribution | Byte loop | MVE | Speedup |
|---|---:|---:|---:|
| GoogleA | 126 | 49 | 2.57× |
| GoogleB | 397 | 83 | 4.78× |
| GoogleD | 229 | 57 | 4.02× |
| GoogleL | 193 | 60 | 3.22× |
| GoogleM | 109 | 46 | 2.37× |
| GoogleQ | 219 | 60 | 3.65× |
| GoogleS | 360 | 76 | 4.74× |
| GoogleU | 245 | 63 | 3.89× |
| GoogleW | 144 | 52 | 2.77× |
| Uniform384To4096 | 21,227 | 2,677 | 7.93× |

Note that there is a small regression (`40` to `55` cycles) in first-byte being zero cases.

The assembly remains short in terms of space:

```asm
memcmp:                                 @ @<!-- -->memcmp
	.fnstart
@ %bb.0:
	.p2align	3
.LBB0_1:                                @ =>This Inner Loop Header: Depth=1
	cbz	r2, .LBB0_4
@ %bb.2:                                @   in Loop: Header=BB0_1 Depth=1
	vctp.8	r2
	vpsttt	
	vldrbt.u8	q0, [r0]
	vldrbt.u8	q1, [r1]
	vcmpt.i8	ne, q0, q1
	vmrs	r3, p0
	cbnz	r3, .LBB0_5
@ %bb.3:                                @   in Loop: Header=BB0_1 Depth=1
	adds	r0, #<!-- -->16
	cmp	r2, #<!-- -->16
	add.w	r1, r1, #<!-- -->16
	sub.w	r2, r2, #<!-- -->16
	bhs	.LBB0_1
.LBB0_4:
	movs	r0, #<!-- -->0
	bx	lr
.LBB0_5:
	vmrs	r2, p0
	rbit	r2, r2
	clz	r2, r2
	ldrb	r0, [r0, r2]
	ldrb	r1, [r1, r2]
	subs	r0, r0, r1
	bx	lr
```

Assisted-by: Codex with gpt-6-astra

---
Full diff: https://github.com/llvm/llvm-project/pull/224857.diff


3 Files Affected:

- (modified) libc/src/string/memory_utils/CMakeLists.txt (+2) 
- (added) libc/src/string/memory_utils/arm/inline_memcmp.h (+64) 
- (modified) libc/src/string/memory_utils/inline_memcmp.h (+3) 


``````````diff
diff --git a/libc/src/string/memory_utils/CMakeLists.txt b/libc/src/string/memory_utils/CMakeLists.txt
index 510546d79988a..17da91e15a686 100644
--- a/libc/src/string/memory_utils/CMakeLists.txt
+++ b/libc/src/string/memory_utils/CMakeLists.txt
@@ -10,6 +10,7 @@ add_header_library(
     arm/common.h
     arm/inline_memcpy.h
     arm/inline_memset.h
+    arm/inline_memcmp.h
     generic/aligned_access.h
     generic/byte_per_byte.h
     inline_bcmp.h
@@ -44,6 +45,7 @@ add_header_library(
     libc.src.__support.macros.optimization
     libc.src.__support.macros.properties.architectures
     libc.src.__support.macros.properties.compiler
+    libc.src.__support.macros.properties.cpu_features
 )
 
 add_header_library(
diff --git a/libc/src/string/memory_utils/arm/inline_memcmp.h b/libc/src/string/memory_utils/arm/inline_memcmp.h
new file mode 100644
index 0000000000000..96f2b906f2bb4
--- /dev/null
+++ b/libc/src/string/memory_utils/arm/inline_memcmp.h
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the memcmp implementations for arm.
+///
+///===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ARM_INLINE_MEMCMP_H
+#define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ARM_INLINE_MEMCMP_H
+
+#include "src/__support/CPP/bit.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/cpu_features.h"
+#include "src/__support/math_extras.h"
+#include "src/string/memory_utils/generic/byte_per_byte.h"
+#include "src/string/memory_utils/utils.h"
+
+#if defined(LIBC_TARGET_CPU_HAS_MVE)
+#include <arm_mve.h>
+#endif
+
+namespace LIBC_NAMESPACE_DECL {
+// We limit code size and don't aggressively expand the loop to multiway
+// parallel comparison.
+[[maybe_unused]] LIBC_INLINE MemcmpReturnType inline_memcmp_arm(CPtr p1,
+                                                                CPtr p2,
+                                                                size_t count) {
+#if defined(LIBC_TARGET_CPU_HAS_MVE)
+  // Cast to raw address to avoid ub:expr.add.out.of.bounds
+  uintptr_t p1_addr = cpp::bit_cast<uintptr_t>(p1);
+  uintptr_t p2_addr = cpp::bit_cast<uintptr_t>(p2);
+  while (count != 0) {
+    // Predication handles the final partial vector without reading past count.
+    mve_pred16_t active = vctp8q(count);
+    uint8x16_t a = vldrbq_z_u8(cpp::bit_cast<const uint8_t *>(p1_addr), active);
+    uint8x16_t b = vldrbq_z_u8(cpp::bit_cast<const uint8_t *>(p2_addr), active);
+    unsigned mismatches = vcmpneq_m_u8(a, b, active);
+    if (mismatches != 0) {
+      const size_t offset = cpp::countr_zero(mismatches);
+      return static_cast<int32_t>(cpp::bit_cast<CPtr>(p1_addr)[offset]) -
+             static_cast<int32_t>(cpp::bit_cast<CPtr>(p2_addr)[offset]);
+    }
+    // optimistically increase the address
+    p1_addr += 16;
+    p2_addr += 16;
+    if (sub_overflow(count, size_t{16}, count))
+      break;
+  }
+  return MemcmpReturnType::zero();
+#else
+  return inline_memcmp_byte_per_byte(p1, p2, count);
+#endif
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STRING_MEMORY_UTILS_ARM_INLINE_MEMCMP_H
diff --git a/libc/src/string/memory_utils/inline_memcmp.h b/libc/src/string/memory_utils/inline_memcmp.h
index a98f7ac51648e..ac071f2ac2309 100644
--- a/libc/src/string/memory_utils/inline_memcmp.h
+++ b/libc/src/string/memory_utils/inline_memcmp.h
@@ -21,6 +21,9 @@
 #elif defined(LIBC_TARGET_ARCH_IS_X86)
 #include "src/string/memory_utils/x86_64/inline_memcmp.h"
 #define LIBC_SRC_STRING_MEMORY_UTILS_MEMCMP inline_memcmp_x86
+#elif defined(LIBC_TARGET_ARCH_IS_ARM)
+#include "src/string/memory_utils/arm/inline_memcmp.h"
+#define LIBC_SRC_STRING_MEMORY_UTILS_MEMCMP inline_memcmp_arm
 #elif defined(LIBC_TARGET_ARCH_IS_AARCH64)
 #include "src/string/memory_utils/aarch64/inline_memcmp.h"
 #define LIBC_SRC_STRING_MEMORY_UTILS_MEMCMP inline_memcmp_aarch64_dispatch

``````````

</details>


https://github.com/llvm/llvm-project/pull/224857


More information about the libc-commits mailing list