master
1#include <string.h>
2#include <stdint.h>
3#include <limits.h>
4
5#ifdef __wasm_simd128__
6#include <wasm_simd128.h>
7#endif
8
9#define ALIGN (sizeof(size_t))
10#define ONES ((size_t)-1/UCHAR_MAX)
11#define HIGHS (ONES * (UCHAR_MAX/2+1))
12#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
13
14char *__strchrnul(const char *s, int c)
15{
16 c = (unsigned char)c;
17 if (!c) return (char *)s + strlen(s);
18
19#if defined(__wasm_simd128__) && defined(__wasilibc_simd_string)
20 // Skip Clang 19 and Clang 20 which have a bug (llvm/llvm-project#146574)
21 // which results in an ICE when inline assembly is used with a vector result.
22#if __clang_major__ != 19 && __clang_major__ != 20
23 // Note that reading before/after the allocation of a pointer is UB in
24 // C, so inline assembly is used to generate the exact machine
25 // instruction we want with opaque semantics to the compiler to avoid
26 // the UB.
27 uintptr_t align = (uintptr_t)s % sizeof(v128_t);
28 uintptr_t addr = (uintptr_t)s - align;
29 v128_t vc = wasm_i8x16_splat(c);
30
31 for (;;) {
32 v128_t v;
33 __asm__ (
34 "local.get %1\n"
35 "v128.load 0\n"
36 "local.set %0\n"
37 : "=r"(v)
38 : "r"(addr)
39 : "memory");
40 const v128_t cmp = wasm_i8x16_eq(v, (v128_t){}) | wasm_i8x16_eq(v, vc);
41 // Bitmask is slow on AArch64, any_true is much faster.
42 if (wasm_v128_any_true(cmp)) {
43 // Clear the bits corresponding to align (little-endian)
44 // so we can count trailing zeros.
45 int mask = wasm_i8x16_bitmask(cmp) >> align << align;
46 // At least one bit will be set, unless align cleared them.
47 // Knowing this helps the compiler if it unrolls the loop.
48 __builtin_assume(mask || align);
49 // If the mask became zero because of align,
50 // it's as if we didn't find anything.
51 if (mask) {
52 // Find the offset of the first one bit (little-endian).
53 return (char *)s + (addr - (uintptr_t)s + __builtin_ctz(mask));
54 }
55 }
56 align = 0;
57 addr += sizeof(v128_t);
58 }
59#endif
60#endif
61
62#ifdef __GNUC__
63 typedef size_t __attribute__((__may_alias__)) word;
64 const word *w;
65 for (; (uintptr_t)s % ALIGN; s++)
66 if (!*s || *(unsigned char *)s == c) return (char *)s;
67 size_t k = ONES * c;
68 for (w = (void *)s; !HASZERO(*w) && !HASZERO(*w^k); w++);
69 s = (void *)w;
70#endif
71 for (; *s && *(unsigned char *)s != c; s++);
72 return (char *)s;
73}
74
75weak_alias(__strchrnul, strchrnul);