blob: 44c1917a20817aabad48c53e40bf33b48f66b42e [file] [log] [blame]
xf.libdd93d52023-05-12 07:10:14 -07001/* strrchr: find the last instance of a character in a string.
2
3 Copyright (C) 2014-2016 Free Software Foundation, Inc.
4
5 This file is part of the GNU C Library.
6
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with the GNU C Library. If not, see
19 <http://www.gnu.org/licenses/>. */
20
21#include <sysdep.h>
22
23/* Assumptions:
24 *
25 * ARMv8-a, AArch64
26 * Neon Available.
27 */
28
29/* Arguments and results. */
30#define srcin x0
31#define chrin w1
32
33#define result x0
34
35#define src x2
36#define tmp1 x3
37#define wtmp2 w4
38#define tmp3 x5
39#define src_match x6
40#define src_offset x7
41#define const_m1 x8
42#define tmp4 x9
43#define nul_match x10
44#define chr_match x11
45
46#define vrepchr v0
47#define vdata1 v1
48#define vdata2 v2
49#define vhas_nul1 v3
50#define vhas_nul2 v4
51#define vhas_chr1 v5
52#define vhas_chr2 v6
53#define vrepmask_0 v7
54#define vrepmask_c v16
55#define vend1 v17
56#define vend2 v18
57
58/* Core algorithm.
59
60 For each 32-byte hunk we calculate a 64-bit syndrome value, with
61 two bits per byte (LSB is always in bits 0 and 1, for both big
62 and little-endian systems). For each tuple, bit 0 is set iff
63 the relevant byte matched the requested character; bit 1 is set
64 iff the relevant byte matched the NUL end of string (we trigger
65 off bit0 for the special case of looking for NUL). Since the bits
66 in the syndrome reflect exactly the order in which things occur
67 in the original string a count_trailing_zeros() operation will
68 identify exactly which byte is causing the termination, and why. */
69
70ENTRY(strrchr)
71 cbz x1, L(null_search)
72 /* Magic constant 0x40100401 to allow us to identify which lane
73 matches the requested byte. Magic constant 0x80200802 used
74 similarly for NUL termination. */
75 mov wtmp2, #0x0401
76 movk wtmp2, #0x4010, lsl #16
77 dup vrepchr.16b, chrin
78 bic src, srcin, #31 /* Work with aligned 32-byte hunks. */
79 dup vrepmask_c.4s, wtmp2
80 mov src_offset, #0
81 ands tmp1, srcin, #31
82 add vrepmask_0.4s, vrepmask_c.4s, vrepmask_c.4s /* equiv: lsl #1 */
83 b.eq L(aligned)
84
85 /* Input string is not 32-byte aligned. Rather than forcing
86 the padding bytes to a safe value, we calculate the syndrome
87 for all the bytes, but then mask off those bits of the
88 syndrome that are related to the padding. */
89 ld1 {vdata1.16b, vdata2.16b}, [src], #32
90 neg tmp1, tmp1
91 cmeq vhas_nul1.16b, vdata1.16b, #0
92 cmeq vhas_chr1.16b, vdata1.16b, vrepchr.16b
93 cmeq vhas_nul2.16b, vdata2.16b, #0
94 cmeq vhas_chr2.16b, vdata2.16b, vrepchr.16b
95 and vhas_nul1.16b, vhas_nul1.16b, vrepmask_0.16b
96 and vhas_chr1.16b, vhas_chr1.16b, vrepmask_c.16b
97 and vhas_nul2.16b, vhas_nul2.16b, vrepmask_0.16b
98 and vhas_chr2.16b, vhas_chr2.16b, vrepmask_c.16b
99 addp vhas_nul1.16b, vhas_nul1.16b, vhas_nul2.16b // 256->128
100 addp vhas_chr1.16b, vhas_chr1.16b, vhas_chr2.16b // 256->128
101 addp vhas_nul1.16b, vhas_nul1.16b, vhas_nul1.16b // 128->64
102 addp vhas_chr1.16b, vhas_chr1.16b, vhas_chr1.16b // 128->64
103 mov nul_match, vhas_nul1.2d[0]
104 lsl tmp1, tmp1, #1
105 mov const_m1, #~0
106 mov chr_match, vhas_chr1.2d[0]
107 lsr tmp3, const_m1, tmp1
108
109 bic nul_match, nul_match, tmp3 // Mask padding bits.
110 bic chr_match, chr_match, tmp3 // Mask padding bits.
111 cbnz nul_match, L(tail)
112
113L(loop):
114 cmp chr_match, #0
115 csel src_match, src, src_match, ne
116 csel src_offset, chr_match, src_offset, ne
117L(aligned):
118 ld1 {vdata1.16b, vdata2.16b}, [src], #32
119 cmeq vhas_nul1.16b, vdata1.16b, #0
120 cmeq vhas_chr1.16b, vdata1.16b, vrepchr.16b
121 cmeq vhas_nul2.16b, vdata2.16b, #0
122 cmeq vhas_chr2.16b, vdata2.16b, vrepchr.16b
123 addp vend1.16b, vhas_nul1.16b, vhas_nul2.16b // 256->128
124 and vhas_chr1.16b, vhas_chr1.16b, vrepmask_c.16b
125 and vhas_chr2.16b, vhas_chr2.16b, vrepmask_c.16b
126 addp vhas_chr1.16b, vhas_chr1.16b, vhas_chr2.16b // 256->128
127 addp vend1.16b, vend1.16b, vend1.16b // 128->64
128 addp vhas_chr1.16b, vhas_chr1.16b, vhas_chr1.16b // 128->64
129 mov nul_match, vend1.2d[0]
130 mov chr_match, vhas_chr1.2d[0]
131 cbz nul_match, L(loop)
132
133 and vhas_nul1.16b, vhas_nul1.16b, vrepmask_0.16b
134 and vhas_nul2.16b, vhas_nul2.16b, vrepmask_0.16b
135 addp vhas_nul1.16b, vhas_nul1.16b, vhas_nul2.16b
136 addp vhas_nul1.16b, vhas_nul1.16b, vhas_nul1.16b
137 mov nul_match, vhas_nul1.2d[0]
138
139L(tail):
140 /* Work out exactly where the string ends. */
141 sub tmp4, nul_match, #1
142 eor tmp4, tmp4, nul_match
143 ands chr_match, chr_match, tmp4
144 /* And pick the values corresponding to the last match. */
145 csel src_match, src, src_match, ne
146 csel src_offset, chr_match, src_offset, ne
147
148 /* Count down from the top of the syndrome to find the last match. */
149 clz tmp3, src_offset
150 /* Src_match points beyond the word containing the match, so we can
151 simply subtract half the bit-offset into the syndrome. Because
152 we are counting down, we need to go back one more character. */
153 add tmp3, tmp3, #2
154 sub result, src_match, tmp3, lsr #1
155 /* But if the syndrome shows no match was found, then return NULL. */
156 cmp src_offset, #0
157 csel result, result, xzr, ne
158
159 ret
160L(null_search):
161 b __strchrnul
162
163END(strrchr)
164weak_alias (strrchr, rindex)
165libc_hidden_builtin_def (strrchr)