1 /* Optimized strlen implementation for POWER10 LE.
2 Copyright (C) 2021-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
21 /* To reuse the code for rawmemchr, we have some extra steps compared to the
22 strlen implementation:
23 - Sum the initial value of r3 with the position at which the char was
24 found, to guarantee we return a pointer and not the length.
25 - In the main loop, subtract each byte by the char we are looking for,
26 so we can keep using vminub to quickly check 64B at once. */
27 #ifdef USE_AS_RAWMEMCHR
29 # define FUNCNAME __rawmemchr
31 # define FUNCNAME RAWMEMCHR
33 # define MCOUNT_NARGS 2
34 # define VREG_ZERO v20
35 # define OFF_START_LOOP 256
36 # define RAWMEMCHR_SUBTRACT_VECTORS \
41 # define TAIL(vreg,increment) \
43 addi r4,r4,increment; \
50 # define FUNCNAME __strlen
51 # define DEFINE_STRLEN_HIDDEN_DEF 1
53 # define FUNCNAME STRLEN
55 # define MCOUNT_NARGS 1
56 # define VREG_ZERO v18
57 # define OFF_START_LOOP 192
58 # define TAIL(vreg,increment) \
61 addi r4,r4,increment; \
64 #endif /* USE_AS_RAWMEMCHR */
66 /* TODO: Replace macros by the actual instructions when minimum binutils becomes
67 >= 2.35. This is used to keep compatibility with older versions. */
68 #define VEXTRACTBM(rt,vrb) \
75 #define LXVP(xtp,dq,ra) \
77 | ((((xtp)-32)>>1)<<(32-10)) \
82 #define CHECK16(vreg,offset,addr,label) \
83 lxv vreg+32,offset(addr); \
84 vcmpequb. vreg,vreg,v18; \
87 /* Load 4 quadwords, merge into one VR for speed and check for NULLs. r6 has #
88 of bytes already checked. */
89 #define CHECK64(offset,addr,label) \
91 LXVP(v4+32,offset,addr); \
92 LXVP(v6+32,offset+32,addr); \
93 RAWMEMCHR_SUBTRACT_VECTORS; \
97 vcmpequb. v0,v16,VREG_ZERO; \
100 /* Implements the function
102 int [r3] strlen (const void *s [r3])
104 but when USE_AS_RAWMEMCHR is set, implements the function
106 void* [r3] rawmemchr (const void *s [r3], int c [r4])
108 The implementation can load bytes past a matching byte, but only
109 up to the next 64B boundary, so it never crosses a page. */
113 ENTRY_TOCLESS (FUNCNAME, 4)
114 CALL_MCOUNT MCOUNT_NARGS
116 #ifdef USE_AS_RAWMEMCHR
119 mtvsrd v18+32,r4 /* matching char in v18 */
120 mtvsrd v19+32,r5 /* non matching char in v19 */
122 vspltb v18,v18,7 /* replicate */
123 vspltb v19,v19,7 /* replicate */
129 /* Next 16B-aligned address. Prepare address for L(aligned). */
133 /* Align data and fill bytes not loaded with non matching char. */
141 #ifdef USE_AS_RAWMEMCHR
149 /* Test up to OFF_START_LOOP-16 bytes in 16B chunks. The main loop is
150 optimized for longer strings, so checking the first bytes in 16B
151 chunks benefits a lot small strings. */
154 #ifdef USE_AS_RAWMEMCHR
155 cmpdi cr5,r4,0 /* Check if c == 0. This will be useful to
156 choose how we will perform the main loop. */
158 /* Prepare address for the loop. */
159 addi r4,r3,OFF_START_LOOP
162 CHECK16(v0,0,r5,tail1)
163 CHECK16(v1,16,r5,tail2)
164 CHECK16(v2,32,r5,tail3)
165 CHECK16(v3,48,r5,tail4)
166 CHECK16(v4,64,r5,tail5)
167 CHECK16(v5,80,r5,tail6)
168 CHECK16(v6,96,r5,tail7)
169 CHECK16(v7,112,r5,tail8)
170 CHECK16(v8,128,r5,tail9)
171 CHECK16(v9,144,r5,tail10)
172 CHECK16(v10,160,r5,tail11)
173 #ifdef USE_AS_RAWMEMCHR
174 CHECK16(v0,176,r5,tail12)
175 CHECK16(v1,192,r5,tail13)
176 CHECK16(v2,208,r5,tail14)
177 CHECK16(v3,224,r5,tail15)
182 #ifdef USE_AS_RAWMEMCHR
183 /* If c == 0, use the same loop as strlen, without the vsububm. */
186 /* This is very similar to the block after L(loop), the difference is
187 that here RAWMEMCHR_SUBTRACT_VECTORS is not empty, and we subtract
188 each byte loaded by the char we are looking for, this way we can keep
189 using vminub to merge the results and checking for nulls. */
192 CHECK64(0,r4,pre_tail_64b)
193 CHECK64(64,r4,pre_tail_64b)
196 CHECK64(0,r5,tail_64b)
197 CHECK64(64,r5,tail_64b)
202 /* Switch to a more aggressive approach checking 64B each time. Use 2
203 pointers 128B apart and unroll the loop once to make the pointer
204 updates and usages separated enough to avoid stalls waiting for
205 address calculation. */
208 #undef RAWMEMCHR_SUBTRACT_VECTORS
209 #define RAWMEMCHR_SUBTRACT_VECTORS /* nothing */
210 CHECK64(0,r4,pre_tail_64b)
211 CHECK64(64,r4,pre_tail_64b)
214 CHECK64(0,r5,tail_64b)
215 CHECK64(64,r5,tail_64b)
224 /* OK, we found a null byte. Let's look for it in the current 64-byte
225 block and mark it in its corresponding VR. lxvp vx,0(ry) puts the
226 low 16B bytes into vx+1, and the high into vx, so the order here is
228 vcmpequb v1,v5,VREG_ZERO
229 vcmpequb v2,v4,VREG_ZERO
230 vcmpequb v3,v7,VREG_ZERO
231 vcmpequb v4,v6,VREG_ZERO
233 /* Take into account the other 64B blocks we had already checked. */
236 /* Extract first bit of each byte. */
242 /* Shift each value into their corresponding position. */
247 /* Merge the results. */
252 cnttzd r0,r10 /* Count trailing zeros before the match. */
253 #ifndef USE_AS_RAWMEMCHR
256 add r3,r5,r0 /* Compute final length. */
303 #ifdef USE_AS_RAWMEMCHR
323 #ifdef USE_AS_RAWMEMCHR
324 weak_alias (__rawmemchr,rawmemchr)
325 libc_hidden_builtin_def (__rawmemchr)
327 # ifdef DEFINE_STRLEN_HIDDEN_DEF
328 weak_alias (__strlen, strlen)
329 libc_hidden_builtin_def (strlen)