x86: Code cleanup in strchr-evex and comment justifying branch
[glibc.git] / sysdeps / x86_64 / multiarch / strchr-evex.S
blobec739fb8f9f2f251f07d620df099ef418ee10233
1 /* strchr/strchrnul optimized with 256-bit EVEX instructions.
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/>.  */
19 #if IS_IN (libc)
21 # include <sysdep.h>
23 # ifndef STRCHR
24 #  define STRCHR        __strchr_evex
25 # endif
27 # define VMOVU          vmovdqu64
28 # define VMOVA          vmovdqa64
30 # ifdef USE_AS_WCSCHR
31 #  define VPBROADCAST   vpbroadcastd
32 #  define VPCMP         vpcmpd
33 #  define VPTESTN       vptestnmd
34 #  define VPMINU        vpminud
35 #  define CHAR_REG      esi
36 #  define SHIFT_REG     ecx
37 #  define CHAR_SIZE     4
38 # else
39 #  define VPBROADCAST   vpbroadcastb
40 #  define VPCMP         vpcmpb
41 #  define VPTESTN       vptestnmb
42 #  define VPMINU        vpminub
43 #  define CHAR_REG      sil
44 #  define SHIFT_REG     edx
45 #  define CHAR_SIZE     1
46 # endif
48 # define XMMZERO        xmm16
50 # define YMMZERO        ymm16
51 # define YMM0           ymm17
52 # define YMM1           ymm18
53 # define YMM2           ymm19
54 # define YMM3           ymm20
55 # define YMM4           ymm21
56 # define YMM5           ymm22
57 # define YMM6           ymm23
58 # define YMM7           ymm24
59 # define YMM8           ymm25
61 # define VEC_SIZE 32
62 # define PAGE_SIZE 4096
63 # define CHAR_PER_VEC (VEC_SIZE / CHAR_SIZE)
65         .section .text.evex,"ax",@progbits
66 ENTRY_P2ALIGN (STRCHR, 5)
67         /* Broadcast CHAR to YMM0.      */
68         VPBROADCAST     %esi, %YMM0
69         movl    %edi, %eax
70         andl    $(PAGE_SIZE - 1), %eax
71         /* Check if we cross page boundary with one vector load.
72            Otherwise it is safe to use an unaligned load.  */
73         cmpl    $(PAGE_SIZE - VEC_SIZE), %eax
74         ja      L(cross_page_boundary)
76         /* Check the first VEC_SIZE bytes. Search for both CHAR and the
77            null bytes.  */
78         VMOVU   (%rdi), %YMM1
80         /* Leaves only CHARS matching esi as 0.  */
81         vpxorq  %YMM1, %YMM0, %YMM2
82         VPMINU  %YMM2, %YMM1, %YMM2
83         /* Each bit in K0 represents a CHAR or a null byte in YMM1.  */
84         VPTESTN %YMM2, %YMM2, %k0
85         kmovd   %k0, %eax
86         testl   %eax, %eax
87         jz      L(aligned_more)
88         tzcntl  %eax, %eax
89 # ifndef USE_AS_STRCHRNUL
90         /* Found CHAR or the null byte.  */
91         cmp     (%rdi, %rax, CHAR_SIZE), %CHAR_REG
92         /* NB: Use a branch instead of cmovcc here. The expectation is
93            that with strchr the user will branch based on input being
94            null. Since this branch will be 100% predictive of the user
95            branch a branch miss here should save what otherwise would
96            be branch miss in the user code. Otherwise using a branch 1)
97            saves code size and 2) is faster in highly predictable
98            environments.  */
99         jne     L(zero)
100 # endif
101 # ifdef USE_AS_WCSCHR
102         /* NB: Multiply wchar_t count by 4 to get the number of bytes.
103          */
104         leaq    (%rdi, %rax, CHAR_SIZE), %rax
105 # else
106         addq    %rdi, %rax
107 # endif
108         ret
112         .p2align 4,, 10
113 L(first_vec_x4):
114 # ifndef USE_AS_STRCHRNUL
115         /* Check to see if first match was CHAR (k0) or null (k1).  */
116         kmovd   %k0, %eax
117         tzcntl  %eax, %eax
118         kmovd   %k1, %ecx
119         /* bzhil will not be 0 if first match was null.  */
120         bzhil   %eax, %ecx, %ecx
121         jne     L(zero)
122 # else
123         /* Combine CHAR and null matches.  */
124         kord    %k0, %k1, %k0
125         kmovd   %k0, %eax
126         tzcntl  %eax, %eax
127 # endif
128         /* NB: Multiply sizeof char type (1 or 4) to get the number of
129            bytes.  */
130         leaq    (VEC_SIZE * 4)(%rdi, %rax, CHAR_SIZE), %rax
131         ret
133 # ifndef USE_AS_STRCHRNUL
134 L(zero):
135         xorl    %eax, %eax
136         ret
137 # endif
140         .p2align 4
141 L(first_vec_x1):
142         /* Use bsf here to save 1-byte keeping keeping the block in 1x
143            fetch block. eax guranteed non-zero.  */
144         bsfl    %eax, %eax
145 # ifndef USE_AS_STRCHRNUL
146         /* Found CHAR or the null byte.  */
147         cmp     (VEC_SIZE)(%rdi, %rax, CHAR_SIZE), %CHAR_REG
148         jne     L(zero)
150 # endif
151         /* NB: Multiply sizeof char type (1 or 4) to get the number of
152            bytes.  */
153         leaq    (VEC_SIZE)(%rdi, %rax, CHAR_SIZE), %rax
154         ret
156         .p2align 4,, 10
157 L(first_vec_x2):
158 # ifndef USE_AS_STRCHRNUL
159         /* Check to see if first match was CHAR (k0) or null (k1).  */
160         kmovd   %k0, %eax
161         tzcntl  %eax, %eax
162         kmovd   %k1, %ecx
163         /* bzhil will not be 0 if first match was null.  */
164         bzhil   %eax, %ecx, %ecx
165         jne     L(zero)
166 # else
167         /* Combine CHAR and null matches.  */
168         kord    %k0, %k1, %k0
169         kmovd   %k0, %eax
170         tzcntl  %eax, %eax
171 # endif
172         /* NB: Multiply sizeof char type (1 or 4) to get the number of
173            bytes.  */
174         leaq    (VEC_SIZE * 2)(%rdi, %rax, CHAR_SIZE), %rax
175         ret
177         .p2align 4,, 10
178 L(first_vec_x3):
179         /* Use bsf here to save 1-byte keeping keeping the block in 1x
180            fetch block. eax guranteed non-zero.  */
181         bsfl    %eax, %eax
182 # ifndef USE_AS_STRCHRNUL
183         /* Found CHAR or the null byte.  */
184         cmp     (VEC_SIZE * 3)(%rdi, %rax, CHAR_SIZE), %CHAR_REG
185         jne     L(zero)
186 # endif
187         /* NB: Multiply sizeof char type (1 or 4) to get the number of
188            bytes.  */
189         leaq    (VEC_SIZE * 3)(%rdi, %rax, CHAR_SIZE), %rax
190         ret
192         .p2align 4
193 L(aligned_more):
194         /* Align data to VEC_SIZE.  */
195         andq    $-VEC_SIZE, %rdi
196 L(cross_page_continue):
197         /* Check the next 4 * VEC_SIZE. Only one VEC_SIZE at a time since
198            data is only aligned to VEC_SIZE. Use two alternating methods
199            for checking VEC to balance latency and port contention.  */
201         /* This method has higher latency but has better port
202            distribution.  */
203         VMOVA   (VEC_SIZE)(%rdi), %YMM1
204         /* Leaves only CHARS matching esi as 0.  */
205         vpxorq  %YMM1, %YMM0, %YMM2
206         VPMINU  %YMM2, %YMM1, %YMM2
207         /* Each bit in K0 represents a CHAR or a null byte in YMM1.  */
208         VPTESTN %YMM2, %YMM2, %k0
209         kmovd   %k0, %eax
210         testl   %eax, %eax
211         jnz     L(first_vec_x1)
213         /* This method has higher latency but has better port
214            distribution.  */
215         VMOVA   (VEC_SIZE * 2)(%rdi), %YMM1
216         /* Each bit in K0 represents a CHAR in YMM1.  */
217         VPCMP   $0, %YMM1, %YMM0, %k0
218         /* Each bit in K1 represents a CHAR in YMM1.  */
219         VPTESTN %YMM1, %YMM1, %k1
220         kortestd        %k0, %k1
221         jnz     L(first_vec_x2)
223         VMOVA   (VEC_SIZE * 3)(%rdi), %YMM1
224         /* Leaves only CHARS matching esi as 0.  */
225         vpxorq  %YMM1, %YMM0, %YMM2
226         VPMINU  %YMM2, %YMM1, %YMM2
227         /* Each bit in K0 represents a CHAR or a null byte in YMM1.  */
228         VPTESTN %YMM2, %YMM2, %k0
229         kmovd   %k0, %eax
230         testl   %eax, %eax
231         jnz     L(first_vec_x3)
233         VMOVA   (VEC_SIZE * 4)(%rdi), %YMM1
234         /* Each bit in K0 represents a CHAR in YMM1.  */
235         VPCMP   $0, %YMM1, %YMM0, %k0
236         /* Each bit in K1 represents a CHAR in YMM1.  */
237         VPTESTN %YMM1, %YMM1, %k1
238         kortestd        %k0, %k1
239         jnz     L(first_vec_x4)
241         /* Align data to VEC_SIZE * 4 for the loop.  */
242         addq    $VEC_SIZE, %rdi
243         andq    $-(VEC_SIZE * 4), %rdi
245         .p2align 4
246 L(loop_4x_vec):
247         /* Check 4x VEC at a time. No penalty to imm32 offset with evex
248            encoding.  */
249         VMOVA   (VEC_SIZE * 4)(%rdi), %YMM1
250         VMOVA   (VEC_SIZE * 5)(%rdi), %YMM2
251         VMOVA   (VEC_SIZE * 6)(%rdi), %YMM3
252         VMOVA   (VEC_SIZE * 7)(%rdi), %YMM4
254         /* For YMM1 and YMM3 use xor to set the CHARs matching esi to
255            zero.  */
256         vpxorq  %YMM1, %YMM0, %YMM5
257         /* For YMM2 and YMM4 cmp not equals to CHAR and store result in
258            k register. Its possible to save either 1 or 2 instructions
259            using cmp no equals method for either YMM1 or YMM1 and YMM3
260            respectively but bottleneck on p5 makes it not worth it.  */
261         VPCMP   $4, %YMM0, %YMM2, %k2
262         vpxorq  %YMM3, %YMM0, %YMM7
263         VPCMP   $4, %YMM0, %YMM4, %k4
265         /* Use min to select all zeros from either xor or end of string).
266          */
267         VPMINU  %YMM1, %YMM5, %YMM1
268         VPMINU  %YMM3, %YMM7, %YMM3
270         /* Use min + zeromask to select for zeros. Since k2 and k4 will
271            have 0 as positions that matched with CHAR which will set
272            zero in the corresponding destination bytes in YMM2 / YMM4.
273          */
274         VPMINU  %YMM1, %YMM2, %YMM2{%k2}{z}
275         VPMINU  %YMM3, %YMM4, %YMM4
276         VPMINU  %YMM2, %YMM4, %YMM4{%k4}{z}
278         VPTESTN %YMM4, %YMM4, %k1
279         kmovd   %k1, %ecx
280         subq    $-(VEC_SIZE * 4), %rdi
281         testl   %ecx, %ecx
282         jz      L(loop_4x_vec)
284         VPTESTN %YMM1, %YMM1, %k0
285         kmovd   %k0, %eax
286         testl   %eax, %eax
287         jnz     L(last_vec_x1)
289         VPTESTN %YMM2, %YMM2, %k0
290         kmovd   %k0, %eax
291         testl   %eax, %eax
292         jnz     L(last_vec_x2)
294         VPTESTN %YMM3, %YMM3, %k0
295         kmovd   %k0, %eax
296         /* Combine YMM3 matches (eax) with YMM4 matches (ecx).  */
297 # ifdef USE_AS_WCSCHR
298         sall    $8, %ecx
299         orl     %ecx, %eax
300         bsfl    %eax, %eax
301 # else
302         salq    $32, %rcx
303         orq     %rcx, %rax
304         bsfq    %rax, %rax
305 # endif
306 # ifndef USE_AS_STRCHRNUL
307         /* Check if match was CHAR or null.  */
308         cmp     (VEC_SIZE * 2)(%rdi, %rax, CHAR_SIZE), %CHAR_REG
309         jne     L(zero_end)
310 # endif
311         /* NB: Multiply sizeof char type (1 or 4) to get the number of
312            bytes.  */
313         leaq    (VEC_SIZE * 2)(%rdi, %rax, CHAR_SIZE), %rax
314         ret
316         .p2align 4,, 8
317 L(last_vec_x1):
318         bsfl    %eax, %eax
319 # ifdef USE_AS_WCSCHR
320         /* NB: Multiply wchar_t count by 4 to get the number of bytes.
321            */
322         leaq    (%rdi, %rax, CHAR_SIZE), %rax
323 # else
324         addq    %rdi, %rax
325 # endif
327 # ifndef USE_AS_STRCHRNUL
328         /* Check if match was null.  */
329         cmp     (%rax), %CHAR_REG
330         jne     L(zero_end)
331 # endif
333         ret
335         .p2align 4,, 8
336 L(last_vec_x2):
337         bsfl    %eax, %eax
338 # ifndef USE_AS_STRCHRNUL
339         /* Check if match was null.  */
340         cmp     (VEC_SIZE)(%rdi, %rax, CHAR_SIZE), %CHAR_REG
341         jne     L(zero_end)
342 # endif
343         /* NB: Multiply sizeof char type (1 or 4) to get the number of
344            bytes.  */
345         leaq    (VEC_SIZE)(%rdi, %rax, CHAR_SIZE), %rax
346         ret
348         /* Cold case for crossing page with first load.  */
349         .p2align 4,, 8
350 L(cross_page_boundary):
351         movq    %rdi, %rdx
352         /* Align rdi.  */
353         andq    $-VEC_SIZE, %rdi
354         VMOVA   (%rdi), %YMM1
355         /* Leaves only CHARS matching esi as 0.  */
356         vpxorq  %YMM1, %YMM0, %YMM2
357         VPMINU  %YMM2, %YMM1, %YMM2
358         /* Each bit in K0 represents a CHAR or a null byte in YMM1.  */
359         VPTESTN %YMM2, %YMM2, %k0
360         kmovd   %k0, %eax
361         /* Remove the leading bits.  */
362 # ifdef USE_AS_WCSCHR
363         movl    %edx, %SHIFT_REG
364         /* NB: Divide shift count by 4 since each bit in K1 represent 4
365            bytes.  */
366         sarl    $2, %SHIFT_REG
367         andl    $(CHAR_PER_VEC - 1), %SHIFT_REG
368 # endif
369         sarxl   %SHIFT_REG, %eax, %eax
370         /* If eax is zero continue.  */
371         testl   %eax, %eax
372         jz      L(cross_page_continue)
373         bsfl    %eax, %eax
375 # ifdef USE_AS_WCSCHR
376         /* NB: Multiply wchar_t count by 4 to get the number of
377            bytes.  */
378         leaq    (%rdx, %rax, CHAR_SIZE), %rax
379 # else
380         addq    %rdx, %rax
381 # endif
382 # ifndef USE_AS_STRCHRNUL
383         /* Check to see if match was CHAR or null.  */
384         cmp     (%rax), %CHAR_REG
385         je      L(cross_page_ret)
386 L(zero_end):
387         xorl    %eax, %eax
388 L(cross_page_ret):
389 # endif
390         ret
392 END (STRCHR)
393 #endif