| //===-- Strlen for generic SIMD types -------------------------------------===// |
| // |
| // 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_STRING_MEMORY_UTILS_GENERIC_INLINE_STRLEN_H |
| #define LLVM_LIBC_SRC_STRING_MEMORY_UTILS_GENERIC_INLINE_STRLEN_H |
| |
| #include "src/__support/CPP/bit.h" |
| #include "src/__support/CPP/simd.h" |
| #include "src/__support/common.h" |
| |
| namespace LIBC_NAMESPACE_DECL { |
| namespace clang_vector { |
| |
| // Exploit the underlying integer representation to do a variable shift. |
| template <typename byte_ty> |
| LIBC_INLINE constexpr cpp::simd_mask<byte_ty> shift_mask(cpp::simd_mask<char> m, |
| size_t shift) { |
| using bitmask_ty = cpp::internal::get_as_integer_type_t<cpp::simd_mask<char>>; |
| bitmask_ty r = cpp::bit_cast<bitmask_ty>(m) >> shift; |
| return cpp::bit_cast<cpp::simd_mask<byte_ty>>(r); |
| } |
| |
| LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE size_t string_length(const char *src) { |
| constexpr cpp::simd<char> null_byte = cpp::splat('\0'); |
| |
| size_t alignment = alignof(cpp::simd<char>); |
| const cpp::simd<char> *aligned = reinterpret_cast<const cpp::simd<char> *>( |
| __builtin_align_down(src, alignment)); |
| |
| cpp::simd<char> chars = cpp::load<cpp::simd<char>>(aligned, /*aligned=*/true); |
| cpp::simd_mask<char> mask = chars == null_byte; |
| size_t offset = src - reinterpret_cast<const char *>(aligned); |
| if (cpp::any_of(shift_mask<char>(mask, offset))) |
| return cpp::find_first_set(shift_mask<char>(mask, offset)); |
| |
| for (;;) { |
| cpp::simd<char> chars = cpp::load<cpp::simd<char>>(++aligned, |
| /*aligned=*/true); |
| cpp::simd_mask<char> mask = chars == null_byte; |
| if (cpp::any_of(mask)) |
| return (reinterpret_cast<const char *>(aligned) - src) + |
| cpp::find_first_set(mask); |
| } |
| } |
| |
| 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) |
| return nullptr; |
| return const_cast<char *>(src) + c_offset; |
| } |
| |
| LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE static void * |
| find_first_character(const unsigned char *s, unsigned char c, size_t n) { |
| using Vector = cpp::simd<char>; |
| using Mask = cpp::simd_mask<char>; |
| Vector c_byte = c; |
| |
| size_t alignment = alignof(Vector); |
| const Vector *aligned = |
| reinterpret_cast<const Vector *>(__builtin_align_down(s, alignment)); |
| |
| Vector chars = cpp::load<Vector>(aligned, /*aligned=*/true); |
| Mask cmp_v = chars == c_byte; |
| size_t offset = s - reinterpret_cast<const unsigned char *>(aligned); |
| |
| cmp_v = shift_mask<unsigned char>(cmp_v, offset); |
| if (cpp::any_of(cmp_v)) |
| return calculate_find_first_character_return( |
| reinterpret_cast<const char *>(s), cmp_v, n); |
| |
| for (size_t bytes_checked = sizeof(Vector) - offset; bytes_checked < n; |
| bytes_checked += sizeof(Vector)) { |
| aligned++; |
| Vector chars = cpp::load<Vector>(aligned, /*aligned=*/true); |
| Mask cmp_v = chars == c_byte; |
| if (cpp::any_of(cmp_v)) |
| return calculate_find_first_character_return( |
| reinterpret_cast<const char *>(aligned), cmp_v, n - bytes_checked); |
| } |
| return nullptr; |
| } |
| |
| } // namespace clang_vector |
| |
| } // namespace LIBC_NAMESPACE_DECL |
| |
| #endif // LLVM_LIBC_SRC_STRING_MEMORY_UTILS_GENERIC_INLINE_STRLEN_H |