[libc-commits] [libc] [libc] Implement wide read strlen with LLVM vector type (PR #152605)

via libc-commits libc-commits at lists.llvm.org
Thu Aug 7 15:28:51 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Joseph Huber (jhuber6)

<details>
<summary>Changes</summary>

Summary:
This is a PR to show how this could be done cross-platform with LLVM
vectors. The downside is that this only works with LLVM/Clang 15 due to
the needed support for boolean vectors,

It's based off of https://github.com/llvm/llvm-project/pull/152389 and
mostly just shows a common `vector` helper that could be used for
anything SIMD related.


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


7 Files Affected:

- (modified) libc/src/__support/CMakeLists.txt (+9) 
- (modified) libc/src/__support/macros/attributes.h (+8) 
- (modified) libc/src/__support/macros/properties/cpu_features.h (+4) 
- (added) libc/src/__support/vector.h (+93) 
- (modified) libc/src/string/CMakeLists.txt (+1) 
- (modified) libc/src/string/string_utils.h (+39-1) 
- (modified) libc/src/string/strlen.cpp (+3) 


``````````diff
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index 2196d9e23bba7..e2722ed352f71 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -313,6 +313,15 @@ add_header_library(
     libc.src.string.memory_utils.inline_memset
 )
 
+add_header_library(
+  vector
+  HDRS
+    vector.h
+  DEPENDS
+    libc.hdr.stdint_proxy
+    libc.src.__support.macros.attributes
+)
+
 add_header_library(
   char_vector
   HDRS
diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index c6474673de85a..53912dce427d2 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -48,4 +48,12 @@
 #define LIBC_PREFERED_TYPE(TYPE)
 #endif
 
+#if __has_attribute(ext_vector_type) && defined(__clang__) &&                  \
+    __clang_major__ >= 15
+#define LIBC_HAS_VECTOR_TYPE 1
+#define LIBC_VECTOR_TYPE(N) __attribute__((ext_vector_type(N)))
+#else
+#define LIBC_HAS_VECTOR_TYPE 0
+#endif
+
 #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_ATTRIBUTES_H
diff --git a/libc/src/__support/macros/properties/cpu_features.h b/libc/src/__support/macros/properties/cpu_features.h
index fde30eadfd83b..fc6099ca6ccc5 100644
--- a/libc/src/__support/macros/properties/cpu_features.h
+++ b/libc/src/__support/macros/properties/cpu_features.h
@@ -59,6 +59,10 @@
 #endif // LIBC_TARGET_CPU_HAS_ARM_FPU_DOUBLE
 #endif // __ARM_FP
 
+#if defined(__ARM_NEON)
+#define LIBC_TARGET_CPU_HAS_ARM_NEON
+#endif
+
 #if defined(__riscv_flen)
 // https://github.com/riscv-non-isa/riscv-c-api-doc/blob/main/src/c-api.adoc
 #if defined(__riscv_zfhmin)
diff --git a/libc/src/__support/vector.h b/libc/src/__support/vector.h
new file mode 100644
index 0000000000000..581e727349761
--- /dev/null
+++ b/libc/src/__support/vector.h
@@ -0,0 +1,93 @@
+//===-- Helper functions for SIMD extensions --------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_VECTOR_H
+#define LLVM_LIBC_SRC___SUPPORT_VECTOR_H
+
+#include "hdr/stdint_proxy.h"
+#include "src/__support/CPP/bit.h"
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/properties/cpu_features.h"
+
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+static_assert(LIBC_HAS_VECTOR_TYPE, "Compiler does not support vector types.");
+
+namespace vector {
+
+template <size_t N> struct BitmaskTy;
+template <> struct BitmaskTy<1> {
+  using type = uint8_t;
+};
+template <> struct BitmaskTy<8> {
+  using type = uint8_t;
+};
+template <> struct BitmaskTy<16> {
+  using type = uint16_t;
+};
+template <> struct BitmaskTy<32> {
+  using type = uint32_t;
+};
+template <> struct BitmaskTy<64> {
+  using type = uint64_t;
+};
+
+template <typename T> struct Scalar {
+  static constexpr size_t WIDTH = sizeof(T);
+  static constexpr size_t NUM_ELEMENTS = WIDTH / sizeof(T);
+};
+template <typename T> struct SSE2 {
+  static constexpr size_t WIDTH = 16;
+  static constexpr size_t NUM_ELEMENTS = WIDTH / sizeof(T);
+};
+template <typename T> struct AVX2 {
+  static constexpr size_t WIDTH = 32;
+  static constexpr size_t NUM_ELEMENTS = WIDTH / sizeof(T);
+};
+template <typename T> struct AVX512 {
+  static constexpr size_t WIDTH = 64;
+  static constexpr size_t NUM_ELEMENTS = WIDTH / sizeof(T);
+};
+template <typename T> struct Neon {
+  static constexpr size_t WIDTH = 16;
+  static constexpr size_t NUM_ELEMENTS = WIDTH / sizeof(T);
+};
+
+#if defined(LIBC_TARGET_CPU_HAS_AVX512F)
+template <typename T> using Platform = AVX512<T>;
+#elif defined(LIBC_TARGET_CPU_HAS_AVX2)
+template <typename T> using Platform = AVX2<T>;
+#elif defined(LIBC_TARGET_CPU_HAS_SSE2)
+template <typename T> using Platform = SSE2<T>;
+#elif defined(LIBC_TARGET_CPU_HAS_ARM_NEON)
+template <typename T> using Platform = Neon<T>;
+#else
+template <typename T> using Platform = Scalar<T>;
+#endif
+
+template <typename T, size_t N = Platform<T>::NUM_ELEMENTS>
+using Vector = T LIBC_VECTOR_TYPE(N);
+
+template <typename To, typename From, size_t N>
+LIBC_INLINE Vector<To, N> convert(const Vector<From, N> &v) {
+  return __builtin_convertvector(v, Vector<To, N>);
+}
+
+template <typename T, size_t N>
+LIBC_INLINE typename BitmaskTy<N>::type to_bitmask(const Vector<T, N> &v) {
+  return __builtin_bit_cast(typename BitmaskTy<N>::type,
+                            convert<bool, T, N>(v));
+}
+} // namespace vector
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_VECTOR_H
diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt
index 809decfbe5f08..cee1a80d4566a 100644
--- a/libc/src/string/CMakeLists.txt
+++ b/libc/src/string/CMakeLists.txt
@@ -20,6 +20,7 @@ add_header_library(
     libc.hdr.stdint_proxy
     libc.src.__support.CPP.bitset
     libc.src.__support.CPP.type_traits
+    libc.src.__support.CPP.vector
     libc.src.__support.common
   ${string_config_options}
 )
diff --git a/libc/src/string/string_utils.h b/libc/src/string/string_utils.h
index 80e5783c7890b..e2549f55972b5 100644
--- a/libc/src/string/string_utils.h
+++ b/libc/src/string/string_utils.h
@@ -22,6 +22,10 @@
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
 
+#if LIBC_HAS_VECTOR_TYPE
+#include "src/__support/vector.h"
+#endif
+
 namespace LIBC_NAMESPACE_DECL {
 namespace internal {
 
@@ -61,7 +65,7 @@ template <typename Word> LIBC_INLINE constexpr bool has_zeroes(Word block) {
 }
 
 template <typename Word>
-LIBC_INLINE size_t string_length_wide_read(const char *src) {
+LIBC_INLINE size_t string_length_wide_read_chars(const char *src) {
   const char *char_ptr = src;
   // Step 1: read 1 byte at a time to align to block size
   for (; reinterpret_cast<uintptr_t>(char_ptr) % sizeof(Word) != 0;
@@ -81,6 +85,40 @@ LIBC_INLINE size_t string_length_wide_read(const char *src) {
   return static_cast<size_t>(char_ptr - src);
 }
 
+#if LIBC_HAS_VECTOR_TYPE
+LIBC_INLINE size_t string_length_wide_read_vector(const char *src) {
+  using namespace vector;
+
+  const Vector<char> null('\0');
+
+  // Align the pointer to the native vector width and shift out unused byted.
+  const char *aligned = reinterpret_cast<const char *>(
+      reinterpret_cast<uintptr_t>(src) &
+      ~static_cast<uintptr_t>(sizeof(Vector<char>) - 1));
+  const Vector<char> *char_ptr =
+      reinterpret_cast<const Vector<char> *>(aligned);
+  auto bitmask = to_bitmask(*char_ptr == null);
+  if (decltype(bitmask) shifted = bitmask >> (src - aligned))
+    return cpp::countr_zero(shifted);
+
+  // Continue until we find the null byte.
+  for (;;) {
+    ++char_ptr;
+    if (auto bitmask = to_bitmask(*char_ptr == null))
+      return (reinterpret_cast<const char *>(char_ptr) - src) +
+             cpp::countr_zero(bitmask);
+  }
+}
+#endif
+
+LIBC_INLINE size_t string_length_wide_read(const char *src) {
+#if LIBC_HAS_VECTOR_TYPE
+  return string_length_wide_read_vector(src);
+#else
+  return string_length_wide_read_chars(src);
+#endif
+}
+
 // Returns the length of a string, denoted by the first occurrence
 // of a null terminator.
 template <typename T> LIBC_INLINE size_t string_length(const T *src) {
diff --git a/libc/src/string/strlen.cpp b/libc/src/string/strlen.cpp
index 234edb81d4c8c..3cb58df080db5 100644
--- a/libc/src/string/strlen.cpp
+++ b/libc/src/string/strlen.cpp
@@ -11,6 +11,8 @@
 #include "src/__support/macros/null_check.h"
 #include "src/string/string_utils.h"
 
+#include "src/__support/vector.h"
+
 #include "src/__support/common.h"
 
 namespace LIBC_NAMESPACE_DECL {
@@ -19,6 +21,7 @@ namespace LIBC_NAMESPACE_DECL {
 // There might be potential for compiler optimization.
 LLVM_LIBC_FUNCTION(size_t, strlen, (const char *src)) {
   LIBC_CRASH_ON_NULLPTR(src);
+
   return internal::string_length(src);
 }
 

``````````

</details>


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


More information about the libc-commits mailing list