[libc-commits] [libc] c3b7d6f - [libc] Keep __restrict on the Cortex-M memcpy for MVE targets (#233575)
via libc-commits
libc-commits at lists.llvm.org
Mon Oct 19 23:16:24 PDT 2026
Author: Prabhu Rajasekaran
Date: 2026-10-19T23:16:19-07:00
New Revision: c3b7d6f983cc280f17d7d269b812d2fe83539f6a
URL: https://github.com/llvm/llvm-project/commit/c3b7d6f983cc280f17d7d269b812d2fe83539f6a
DIFF: https://github.com/llvm/llvm-project/commit/c3b7d6f983cc280f17d7d269b812d2fe83539f6a.diff
LOG: [libc] Keep __restrict on the Cortex-M memcpy for MVE targets (#233575)
With #226955 the SLP vectorizer versions the Cortex-M memcpy copy loops
with runtime alias checks on MVE targets (270 -> 412 bytes on
Cortex-M55).
Keeping __restrict on MVE targets lets it vectorize without versioning
(222 bytes). Non-MVE targets are unchanged.
Fixes #233426
Added:
Modified:
libc/src/string/memory_utils/arm/inline_memcpy.h
Removed:
################################################################################
diff --git a/libc/src/string/memory_utils/arm/inline_memcpy.h b/libc/src/string/memory_utils/arm/inline_memcpy.h
index 5eddb80d25506..5d226245d12ee 100644
--- a/libc/src/string/memory_utils/arm/inline_memcpy.h
+++ b/libc/src/string/memory_utils/arm/inline_memcpy.h
@@ -139,11 +139,32 @@ copy_bytes_and_bump_pointers(Ptr &dst, CPtr &src, size_t size) {
copy_bytes_and_bump_pointers(dst, src, size);
}
-// Implementation for Cortex-M3, M4, M7, M23, M33, M35P, M52 with hardware
-// support for unaligned loads and stores. It compiles down to 272 bytes when
-// used through `memcpy` that also needs to return the `dst` ptr.
-[[maybe_unused]] LIBC_INLINE void inline_memcpy_arm_mid_end(Ptr dst, CPtr src,
- size_t size) {
+// `memcpy` guarantees that `dst` and `src` do not overlap. Whether the
+// implementation gets to know it is a code size trade-off:
+// - With MVE, the compiler then vectorizes the word by word copies with
+// `vldrb.u8`/`vstrb.8`. Without `__restrict`, the SLP vectorizer versions
+// the copy loops with runtime alias checks and a scalar fallback instead,
+// which is about 50% larger.
+// - Without MVE, the compiler would reorder the word by word loads and stores,
+// which needs more registers and adds push/pop. Hiding `__restrict` keeps
+// the tuned load/store sequence.
+#ifdef __ARM_FEATURE_MVE
+#define LIBC_ARM_MEMCPY_RESTRICT __restrict
+#else
+#define LIBC_ARM_MEMCPY_RESTRICT
+#endif
+
+// Implementation for Cortex-M3, M4, M7, M23, M33, M35P, M52, M55, M85 with
+// hardware support for unaligned loads and stores. It compiles down to 272
+// bytes (222 bytes with MVE) when used through `memcpy` that also needs to
+// return the `dst` ptr.
+[[maybe_unused]] LIBC_INLINE void
+inline_memcpy_arm_mid_end(Ptr LIBC_ARM_MEMCPY_RESTRICT dst_,
+ CPtr LIBC_ARM_MEMCPY_RESTRICT src_, size_t size) {
+ // The helpers take the pointers by reference, which a `__restrict` pointer
+ // cannot bind to.
+ Ptr dst = dst_;
+ CPtr src = src_;
if (misaligned(bitwise_or(src, dst)))
LIBC_ATTR_UNLIKELY {
if (size < 8)
@@ -194,14 +215,11 @@ copy_bytes_and_bump_pointers(Ptr &dst, CPtr &src, size_t size) {
[[maybe_unused]] LIBC_INLINE void inline_memcpy_arm(Ptr dst, CPtr src,
size_t size) {
- // The compiler performs alias analysis and is able to prove that `dst` and
- // `src` do not alias by propagating the `__restrict` keyword from the
- // `memcpy` prototype. This allows the compiler to merge consecutive
- // load/store (LDR, STR) instructions generated in
- // `copy_block_and_bump_pointers` with `BlockOp::kByWord` into load/store
- // double (LDRD, STRD) instructions, this is undesirable so we prevent the
- // compiler from inferring `__restrict` with the following line.
+#ifndef __ARM_FEATURE_MVE
+ // Hide the `__restrict` of the `memcpy` prototype from the implementation,
+ // see `LIBC_ARM_MEMCPY_RESTRICT`.
asm volatile("" : "+r"(dst), "+r"(src));
+#endif
#ifdef __ARM_FEATURE_UNALIGNED
return inline_memcpy_arm_mid_end(dst, src, size);
#else
More information about the libc-commits
mailing list