[libc-commits] [libc] [libc] Don't match the byte after the range in vector find_first_character (PR #235288)
Fabian Meumertzheim via libc-commits
libc-commits at lists.llvm.org
Sat Oct 24 05:26:04 PDT 2026
https://github.com/fmeum created https://github.com/llvm/llvm-project/pull/235288
The vectorized `find_first_character` implementations (`clang_vector` and the x86_64 `arch_vector`) returned the position of a match that was the first byte after the searched range. For example, `memchr("ab", 'b', 1)` returned a pointer to the `'b'` instead of `nullptr`.
The new test covers all implementations, since no configuration selects the vectorized ones by default. On aarch64 Linux, it fails without the fix and passes with it. The x86_64 changes were only compiled, not run.
Assisted-by: Claude Code (Claude Opus 5.5)
>From f5062d84c38e8a7eaba9fd96f24fd092c076196a Mon Sep 17 00:00:00 2001
From: Fabian Meumertzheim <fabian at meumertzhe.im>
Date: Sat, 24 Oct 2026 14:25:08 +0200
Subject: [PATCH] [libc] Don't match the byte after the range in vector
find_first_character
The vectorized find_first_character implementations (clang_vector and
the x86_64 arch_vector) returned the position of a match that was the
first byte after the searched range. For example, memchr("ab", 'b', 1)
returned a pointer to the 'b' instead of nullptr.
The new test covers all implementations, since no configuration selects
the vectorized ones by default.
Assisted-by: Claude Code (Claude Opus 5.5)
---
.../memory_utils/generic/inline_strlen.h | 2 +-
.../memory_utils/x86_64/inline_strlen.h | 2 +-
.../src/strings/wide_read_memory_test.cpp | 33 +++++++++++++++++++
3 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/libc/src/string/memory_utils/generic/inline_strlen.h b/libc/src/string/memory_utils/generic/inline_strlen.h
index 833ff9fbcbf80a..9f84a293478972 100644
--- a/libc/src/string/memory_utils/generic/inline_strlen.h
+++ b/libc/src/string/memory_utils/generic/inline_strlen.h
@@ -50,7 +50,7 @@ LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE size_t string_length(const char *src) {
LIBC_INLINE static void *calculate_find_first_character_return(
const char *src, cpp::simd_mask<char> c_mask, size_t n_left) {
size_t c_offset = cpp::find_first_set(c_mask);
- if (n_left < c_offset)
+ if (n_left <= c_offset)
return nullptr;
return const_cast<char *>(src) + c_offset;
}
diff --git a/libc/src/string/memory_utils/x86_64/inline_strlen.h b/libc/src/string/memory_utils/x86_64/inline_strlen.h
index 0343e22f3e58e3..cc85d9c8fda848 100644
--- a/libc/src/string/memory_utils/x86_64/inline_strlen.h
+++ b/libc/src/string/memory_utils/x86_64/inline_strlen.h
@@ -50,7 +50,7 @@ LIBC_INLINE static void *
calculate_find_first_character_return(const unsigned char *src, Mask c_mask,
size_t n_left) {
size_t c_offset = cpp::countr_zero(c_mask);
- if (n_left < c_offset)
+ if (n_left <= c_offset)
return nullptr;
return const_cast<unsigned char *>(src) + c_offset;
}
diff --git a/libc/test/src/strings/wide_read_memory_test.cpp b/libc/test/src/strings/wide_read_memory_test.cpp
index 7b173d479dc9db..ffe57942b33e39 100644
--- a/libc/test/src/strings/wide_read_memory_test.cpp
+++ b/libc/test/src/strings/wide_read_memory_test.cpp
@@ -120,4 +120,37 @@ TEST_F(LlvmLibcWideAccessMemoryTest, FindFirstChar) {
});
}
+TEST_F(LlvmLibcWideAccessMemoryTest, FindFirstCharAfterRange) {
+ // All implementations, since the configured one is usually not vectorized.
+ static constexpr void *(*kImpls[])(const unsigned char *, unsigned char,
+ size_t) = {
+ internal::element::find_first_character,
+ internal::word::find_first_character,
+#if LIBC_HAS_VECTOR_TYPE
+ clang_vector::find_first_character,
+#endif
+#ifdef LIBC_TARGET_ARCH_IS_X86
+ internal::arch_vector::find_first_character,
+#endif
+ };
+ // 1.5 k long vector of a's.
+ TwoKilobyteBuffer buf;
+ inline_memset(buf.data(), 'a', buf.size());
+ buf[buf.size() - 1] = 'b';
+ this->TestMemoryAccess(buf, [buf](const char *test_data) {
+ const unsigned char *b =
+ reinterpret_cast<const unsigned char *>(test_data) + buf.size() - 1;
+ for (auto find_first_character : kImpls) {
+ // The 'b' right after the searched range must not be found.
+ for (size_t n = 0; n < 128; ++n)
+ ASSERT_EQ(
+ reinterpret_cast<const void *>(find_first_character(b - n, 'b', n)),
+ nullptr);
+ ASSERT_EQ(reinterpret_cast<const void *>(find_first_character(
+ b - (buf.size() - 1), 'b', buf.size() - 1)),
+ nullptr);
+ }
+ });
+}
+
} // namespace LIBC_NAMESPACE_DECL
More information about the libc-commits
mailing list