lib/libc/amd64/string/strcspn.S: always return earliest match in 17--32 char case

When matching against a set of 17--32 characters, strcspn() uses two
invocations of PCMPISTRI to match against the first 16 characters
of the set and then the remaining characters.  If a match was found in
the first half of the set, the code originally immediately returned
that match.  However, it is possible for a match in the second half of
the set to occur earlier in the vector, leading to that match being
overlooked.

Fix the code by checking if there is a match in the second half of the
set and taking the earlier of the two matches.

The correctness of the function has been verified with extended unit
tests and test runs against the glibc test suite.

Approved by:	mjg (implicit, via IRC)
MFC after:	1 week
MFC to:		stable/14
This commit is contained in:
Robert Clausecker 2023-12-19 12:28:57 -05:00
parent a0ecf2224e
commit c91cd7d03a
1 changed files with 24 additions and 3 deletions

View File

@ -255,19 +255,28 @@ ARCHENTRY(strcspn, x86_64_v2)
leave
ret
/* match in first set half during head */
.Lheadmatchv2first:
mov %ecx, %eax
pcmpistri $0, %xmm0, %xmm3 # match in second set half?
cmp %ecx, %eax # before the first half match?
cmova %ecx, %eax # use the earlier match
leave
ret
.Lgt16v2:
movdqu 48(%rsp, %rcx, 1), %xmm3 # second part of set
/* set is 17--32 bytes in size */
pcmpistri $0, %xmm0, %xmm2 # match in first set half?
jb .Lheadmatchv2
jb .Lheadmatchv2first
pcmpistri $0, %xmm0, %xmm3 # match in second set half or end of string?
jbe .Lheadmatchv2
ALIGN_TEXT
0: movdqa (%rax), %xmm0
pcmpistri $0, %xmm0, %xmm2
jb 2f # match in first set half?
jb 4f # match in first set half?
pcmpistri $0, %xmm0, %xmm3
jbe 1f # match in second set half or end of string?
movdqa 16(%rax), %xmm0
@ -277,7 +286,8 @@ ARCHENTRY(strcspn, x86_64_v2)
pcmpistri $0, %xmm0, %xmm3
ja 0b # neither match in 2nd half nor string end?
3: lea -16(%rax), %rax # go back to second half
/* match in second half or NUL */
lea -16(%rax), %rax # go back to second half
1: jc 2f # jump if match found
pxor %xmm1, %xmm1
pcmpeqb %xmm1, %xmm0 # where is the NUL byte?
@ -288,6 +298,17 @@ ARCHENTRY(strcspn, x86_64_v2)
leave
ret
/* match in first half */
3: sub $16, %rax # go back to second half
4: sub %rdi, %rax # offset of %xmm0 from beginning of string
mov %ecx, %edx
pcmpistri $0, %xmm0, %xmm3 # match in second set half?
cmp %ecx, %edx # before the first half match?
cmova %ecx, %edx # use the earlier match
add %rdx, %rax # return full ofset
leave
ret
/* set is empty, degrades to strlen */
.Lstrlenv2:
leave