You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
642 lines
20 KiB
642 lines
20 KiB
# commit 664318c3eb07032e2bfcf47cb2aa3c89280c19e7 |
|
# Author: Alan Modra <amodra@gmail.com> |
|
# Date: Sat Aug 17 18:46:05 2013 +0930 |
|
# |
|
# PowerPC LE strchr |
|
# http://sourceware.org/ml/libc-alpha/2013-08/msg00101.html |
|
# |
|
# Adds little-endian support to optimised strchr assembly. I've also |
|
# tweaked the big-endian code a little. In power7/strchr.S there's a |
|
# check in the tail of the function that we didn't match 0 before |
|
# finding a c match, done by comparing leading zero counts. It's just |
|
# as valid, and quicker, to compare the raw output from cmpb. |
|
# |
|
# Another little tweak is to use rldimi/insrdi in place of rlwimi for |
|
# the power7 strchr functions. Since rlwimi is cracked, it is a few |
|
# cycles slower. rldimi can be used on the 32-bit power7 functions |
|
# too. |
|
# |
|
# * sysdeps/powerpc/powerpc64/power7/strchr.S (strchr): Add little-endian |
|
# support. Correct typos, formatting. Optimize tail. Use insrdi |
|
# rather than rlwimi. |
|
# * sysdeps/powerpc/powerpc32/power7/strchr.S: Likewise. |
|
# * sysdeps/powerpc/powerpc64/power7/strchrnul.S (__strchrnul): Add |
|
# little-endian support. Correct typos. |
|
# * sysdeps/powerpc/powerpc32/power7/strchrnul.S: Likewise. Use insrdi |
|
# rather than rlwimi. |
|
# * sysdeps/powerpc/powerpc64/strchr.S (rTMP4, rTMP5): Define. Use |
|
# in loop and entry code to keep "and." results. |
|
# (strchr): Add little-endian support. Comment. Move cntlzd |
|
# earlier in tail. |
|
# * sysdeps/powerpc/powerpc32/strchr.S: Likewise. |
|
# |
|
Index: glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/power7/strchr.S |
|
=================================================================== |
|
--- glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/power7/strchr.S.orig |
|
+++ glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/power7/strchr.S |
|
@@ -37,8 +37,8 @@ ENTRY (BP_SYM(strchr)) |
|
beq cr7,L(null_match) |
|
|
|
/* Replicate byte to word. */ |
|
- rlwimi r4,r4,8,16,23 |
|
- rlwimi r4,r4,16,0,15 |
|
+ insrdi r4,r4,8,48 |
|
+ insrdi r4,r4,16,32 |
|
|
|
/* Now r4 has a word of c bytes and r0 has |
|
a word of null bytes. */ |
|
@@ -48,11 +48,17 @@ ENTRY (BP_SYM(strchr)) |
|
|
|
/* Move the words left and right to discard the bits that are |
|
not part of the string and to bring them back as zeros. */ |
|
- |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ srw r10,r10,r6 |
|
+ srw r11,r11,r6 |
|
+ slw r10,r10,r6 |
|
+ slw r11,r11,r6 |
|
+#else |
|
slw r10,r10,r6 |
|
slw r11,r11,r6 |
|
srw r10,r10,r6 |
|
srw r11,r11,r6 |
|
+#endif |
|
or r5,r10,r11 /* OR the results to speed things up. */ |
|
cmpwi cr7,r5,0 /* If r5 == 0, no c or null bytes |
|
have been found. */ |
|
@@ -67,7 +73,7 @@ ENTRY (BP_SYM(strchr)) |
|
|
|
/* Handle WORD2 of pair. */ |
|
lwzu r12,4(r8) |
|
- cmpb r10,r12,r4 |
|
+ cmpb r10,r12,r4 |
|
cmpb r11,r12,r0 |
|
or r5,r10,r11 |
|
cmpwi cr7,r5,0 |
|
@@ -102,22 +108,31 @@ L(loop): |
|
bne cr6,L(done) |
|
|
|
/* The c/null byte must be in the second word. Adjust the address |
|
- again and move the result of cmpb to r10 so we can calculate the |
|
- pointer. */ |
|
+ again and move the result of cmpb to r10/r11 so we can calculate |
|
+ the pointer. */ |
|
|
|
mr r10,r6 |
|
mr r11,r7 |
|
addi r8,r8,4 |
|
|
|
- /* r5 has the output of the cmpb instruction, that is, it contains |
|
+ /* r10/r11 have the output of the cmpb instructions, that is, |
|
0xff in the same position as the c/null byte in the original |
|
word from the string. Use that to calculate the pointer. */ |
|
L(done): |
|
- cntlzw r4,r10 /* Count leading zeroes before c matches. */ |
|
- cntlzw r0,r11 /* Count leading zeroes before null matches. */ |
|
- cmplw cr7,r4,r0 |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ addi r3,r10,-1 |
|
+ andc r3,r3,r10 |
|
+ popcntw r0,r3 |
|
+ addi r4,r11,-1 |
|
+ andc r4,r4,r11 |
|
+ cmplw cr7,r3,r4 |
|
+ bgt cr7,L(no_match) |
|
+#else |
|
+ cntlzw r0,r10 /* Count leading zeros before c matches. */ |
|
+ cmplw cr7,r11,r10 |
|
bgt cr7,L(no_match) |
|
- srwi r0,r4,3 /* Convert leading zeroes to bytes. */ |
|
+#endif |
|
+ srwi r0,r0,3 /* Convert leading zeros to bytes. */ |
|
add r3,r8,r0 /* Return address of the matching c byte |
|
or null in case c was not found. */ |
|
blr |
|
@@ -135,10 +150,14 @@ L(null_match): |
|
cmpb r5,r12,r0 /* Compare each byte against null bytes. */ |
|
|
|
/* Move the words left and right to discard the bits that are |
|
- not part of the string and to bring them back as zeros. */ |
|
- |
|
+ not part of the string and bring them back as zeros. */ |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ srw r5,r5,r6 |
|
+ slw r5,r5,r6 |
|
+#else |
|
slw r5,r5,r6 |
|
srw r5,r5,r6 |
|
+#endif |
|
cmpwi cr7,r5,0 /* If r10 == 0, no c or null bytes |
|
have been found. */ |
|
bne cr7,L(done_null) |
|
@@ -193,7 +212,13 @@ L(loop_null): |
|
0xff in the same position as the null byte in the original |
|
word from the string. Use that to calculate the pointer. */ |
|
L(done_null): |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ addi r0,r5,-1 |
|
+ andc r0,r0,r5 |
|
+ popcntw r0,r0 |
|
+#else |
|
cntlzw r0,r5 /* Count leading zeros before the match. */ |
|
+#endif |
|
srwi r0,r0,3 /* Convert leading zeros to bytes. */ |
|
add r3,r8,r0 /* Return address of the matching null byte. */ |
|
blr |
|
Index: glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/power7/strchrnul.S |
|
=================================================================== |
|
--- glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/power7/strchrnul.S.orig |
|
+++ glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/power7/strchrnul.S |
|
@@ -29,8 +29,8 @@ ENTRY (BP_SYM(__strchrnul)) |
|
clrrwi r8,r3,2 /* Align the address to word boundary. */ |
|
|
|
/* Replicate byte to word. */ |
|
- rlwimi r4,r4,8,16,23 |
|
- rlwimi r4,r4,16,0,15 |
|
+ insrdi r4,r4,8,48 |
|
+ insrdi r4,r4,16,32 |
|
|
|
rlwinm r6,r3,3,27,28 /* Calculate padding. */ |
|
lwz r12,0(r8) /* Load word from memory. */ |
|
@@ -45,10 +45,17 @@ ENTRY (BP_SYM(__strchrnul)) |
|
|
|
/* Move the words left and right to discard the bits that are |
|
not part of the string and bring them back as zeros. */ |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ srw r10,r10,r6 |
|
+ srw r9,r9,r6 |
|
+ slw r10,r10,r6 |
|
+ slw r9,r9,r6 |
|
+#else |
|
slw r10,r10,r6 |
|
slw r9,r9,r6 |
|
srw r10,r10,r6 |
|
srw r9,r9,r6 |
|
+#endif |
|
or r5,r9,r10 /* OR the results to speed things up. */ |
|
cmpwi cr7,r5,0 /* If r5 == 0, no c or null bytes |
|
have been found. */ |
|
@@ -56,7 +63,7 @@ ENTRY (BP_SYM(__strchrnul)) |
|
|
|
mtcrf 0x01,r8 |
|
|
|
- /* Are we now aligned to a quadword boundary? If so, skip to |
|
+ /* Are we now aligned to a doubleword boundary? If so, skip to |
|
the main loop. Otherwise, go through the alignment code. */ |
|
|
|
bt 29,L(loop) |
|
@@ -78,7 +85,7 @@ L(loop): |
|
single register for speed. This is an attempt |
|
to speed up the null-checking process for bigger strings. */ |
|
lwz r12,4(r8) |
|
- lwzu r11,8(r8) |
|
+ lwzu r11,8(r8) |
|
cmpb r10,r12,r0 |
|
cmpb r9,r12,r4 |
|
cmpb r6,r11,r0 |
|
@@ -97,9 +104,9 @@ L(loop): |
|
addi r8,r8,-4 |
|
bne cr6,L(done) |
|
|
|
- /* The c/null byte must be in the second word. Adjust the |
|
- address again and move the result of cmpb to r10 so we can calculate |
|
- the pointer. */ |
|
+ /* The c/null byte must be in the second word. Adjust the address |
|
+ again and move the result of cmpb to r5 so we can calculate the |
|
+ pointer. */ |
|
mr r5,r10 |
|
addi r8,r8,4 |
|
|
|
@@ -107,7 +114,13 @@ L(loop): |
|
0xff in the same position as the c/null byte in the original |
|
word from the string. Use that to calculate the pointer. */ |
|
L(done): |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ addi r0,r5,-1 |
|
+ andc r0,r0,r5 |
|
+ popcntw r0,r0 |
|
+#else |
|
cntlzw r0,r5 /* Count leading zeros before the match. */ |
|
+#endif |
|
srwi r0,r0,3 /* Convert leading zeros to bytes. */ |
|
add r3,r8,r0 /* Return address of matching c/null byte. */ |
|
blr |
|
Index: glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/strchr.S |
|
=================================================================== |
|
--- glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/strchr.S.orig |
|
+++ glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/strchr.S |
|
@@ -44,6 +44,8 @@ ENTRY (BP_SYM (strchr)) |
|
#define rIGN r10 /* number of bits we should ignore in the first word */ |
|
#define rMASK r11 /* mask with the bits to ignore set to 0 */ |
|
#define rTMP3 r12 |
|
+#define rTMP4 rIGN |
|
+#define rTMP5 rMASK |
|
|
|
CHECK_BOUNDS_LOW (rSTR, rTMP1, rTMP2) |
|
STORE_RETURN_BOUNDS (rTMP1, rTMP2) |
|
@@ -59,53 +61,74 @@ ENTRY (BP_SYM (strchr)) |
|
addi r7F7F, r7F7F, 0x7f7f |
|
/* Test the first (partial?) word. */ |
|
lwz rWORD, 0(rSTR) |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ slw rMASK, rMASK, rIGN |
|
+#else |
|
srw rMASK, rMASK, rIGN |
|
+#endif |
|
orc rWORD, rWORD, rMASK |
|
add rTMP1, rFEFE, rWORD |
|
nor rTMP2, r7F7F, rWORD |
|
- and. rTMP1, rTMP1, rTMP2 |
|
+ and. rTMP4, rTMP1, rTMP2 |
|
xor rTMP3, rCHR, rWORD |
|
orc rTMP3, rTMP3, rMASK |
|
b L(loopentry) |
|
|
|
/* The loop. */ |
|
|
|
-L(loop):lwzu rWORD, 4(rSTR) |
|
- and. rTMP1, rTMP1, rTMP2 |
|
+L(loop): |
|
+ lwzu rWORD, 4(rSTR) |
|
+ and. rTMP5, rTMP1, rTMP2 |
|
/* Test for 0. */ |
|
- add rTMP1, rFEFE, rWORD |
|
- nor rTMP2, r7F7F, rWORD |
|
+ add rTMP1, rFEFE, rWORD /* x - 0x01010101. */ |
|
+ nor rTMP2, r7F7F, rWORD /* ~(x | 0x7f7f7f7f) == ~x & 0x80808080. */ |
|
bne L(foundit) |
|
- and. rTMP1, rTMP1, rTMP2 |
|
+ and. rTMP4, rTMP1, rTMP2 /* (x - 0x01010101) & ~x & 0x80808080. */ |
|
/* Start test for the bytes we're looking for. */ |
|
xor rTMP3, rCHR, rWORD |
|
L(loopentry): |
|
add rTMP1, rFEFE, rTMP3 |
|
nor rTMP2, r7F7F, rTMP3 |
|
beq L(loop) |
|
+ |
|
/* There is a zero byte in the word, but may also be a matching byte (either |
|
before or after the zero byte). In fact, we may be looking for a |
|
- zero byte, in which case we return a match. We guess that this hasn't |
|
- happened, though. */ |
|
-L(missed): |
|
- and. rTMP1, rTMP1, rTMP2 |
|
+ zero byte, in which case we return a match. */ |
|
+ and. rTMP5, rTMP1, rTMP2 |
|
li rRTN, 0 |
|
STORE_RETURN_VALUE (rSTR) |
|
beqlr |
|
-/* It did happen. Decide which one was first... |
|
- I'm not sure if this is actually faster than a sequence of |
|
- rotates, compares, and branches (we use it anyway because it's shorter). */ |
|
+/* At this point: |
|
+ rTMP5 bytes are 0x80 for each match of c, 0 otherwise. |
|
+ rTMP4 bytes are 0x80 for each match of 0, 0 otherwise. |
|
+ But there may be false matches in the next most significant byte from |
|
+ a true match due to carries. This means we need to recalculate the |
|
+ matches using a longer method for big-endian. */ |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ addi rTMP1, rTMP5, -1 |
|
+ andc rTMP1, rTMP1, rTMP5 |
|
+ cntlzw rCLZB, rTMP1 |
|
+ addi rTMP2, rTMP4, -1 |
|
+ andc rTMP2, rTMP2, rTMP4 |
|
+ cmplw rTMP1, rTMP2 |
|
+ bgtlr |
|
+ subfic rCLZB, rCLZB, 32-7 |
|
+#else |
|
+/* I think we could reduce this by two instructions by keeping the "nor" |
|
+ results from the loop for reuse here. See strlen.S tail. Similarly |
|
+ one instruction could be pruned from L(foundit). */ |
|
and rFEFE, r7F7F, rWORD |
|
- or rMASK, r7F7F, rWORD |
|
+ or rTMP5, r7F7F, rWORD |
|
and rTMP1, r7F7F, rTMP3 |
|
- or rIGN, r7F7F, rTMP3 |
|
+ or rTMP4, r7F7F, rTMP3 |
|
add rFEFE, rFEFE, r7F7F |
|
add rTMP1, rTMP1, r7F7F |
|
- nor rWORD, rMASK, rFEFE |
|
- nor rTMP2, rIGN, rTMP1 |
|
+ nor rWORD, rTMP5, rFEFE |
|
+ nor rTMP2, rTMP4, rTMP1 |
|
+ cntlzw rCLZB, rTMP2 |
|
cmplw rWORD, rTMP2 |
|
bgtlr |
|
- cntlzw rCLZB, rTMP2 |
|
+#endif |
|
srwi rCLZB, rCLZB, 3 |
|
add rRTN, rSTR, rCLZB |
|
CHECK_BOUNDS_HIGH_RTN (rSTR, rTMP2, twlge) |
|
@@ -113,13 +136,21 @@ L(missed): |
|
blr |
|
|
|
L(foundit): |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ addi rTMP1, rTMP5, -1 |
|
+ andc rTMP1, rTMP1, rTMP5 |
|
+ cntlzw rCLZB, rTMP1 |
|
+ subfic rCLZB, rCLZB, 32-7-32 |
|
+ srawi rCLZB, rCLZB, 3 |
|
+#else |
|
and rTMP1, r7F7F, rTMP3 |
|
- or rIGN, r7F7F, rTMP3 |
|
+ or rTMP4, r7F7F, rTMP3 |
|
add rTMP1, rTMP1, r7F7F |
|
- nor rTMP2, rIGN, rTMP1 |
|
+ nor rTMP2, rTMP4, rTMP1 |
|
cntlzw rCLZB, rTMP2 |
|
subi rSTR, rSTR, 4 |
|
srwi rCLZB, rCLZB, 3 |
|
+#endif |
|
add rRTN, rSTR, rCLZB |
|
CHECK_BOUNDS_HIGH_RTN (rSTR, rTMP2, twlge) |
|
STORE_RETURN_VALUE (rSTR) |
|
Index: glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/power7/strchr.S |
|
=================================================================== |
|
--- glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/power7/strchr.S.orig |
|
+++ glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/power7/strchr.S |
|
@@ -37,8 +37,8 @@ ENTRY (BP_SYM(strchr)) |
|
beq cr7,L(null_match) |
|
|
|
/* Replicate byte to doubleword. */ |
|
- rlwimi r4,r4,8,16,23 |
|
- rlwimi r4,r4,16,0,15 |
|
+ insrdi r4,r4,8,48 |
|
+ insrdi r4,r4,16,32 |
|
insrdi r4,r4,32,0 |
|
|
|
/* Now r4 has a doubleword of c bytes and r0 has |
|
@@ -49,11 +49,17 @@ ENTRY (BP_SYM(strchr)) |
|
|
|
/* Move the doublewords left and right to discard the bits that are |
|
not part of the string and bring them back as zeros. */ |
|
- |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ srd r10,r10,r6 |
|
+ srd r11,r11,r6 |
|
+ sld r10,r10,r6 |
|
+ sld r11,r11,r6 |
|
+#else |
|
sld r10,r10,r6 |
|
sld r11,r11,r6 |
|
srd r10,r10,r6 |
|
srd r11,r11,r6 |
|
+#endif |
|
or r5,r10,r11 /* OR the results to speed things up. */ |
|
cmpdi cr7,r5,0 /* If r5 == 0, no c or null bytes |
|
have been found. */ |
|
@@ -110,15 +116,24 @@ L(loop): |
|
mr r11,r7 |
|
addi r8,r8,8 |
|
|
|
- /* r5 has the output of the cmpb instruction, that is, it contains |
|
+ /* r10/r11 have the output of the cmpb instructions, that is, |
|
0xff in the same position as the c/null byte in the original |
|
doubleword from the string. Use that to calculate the pointer. */ |
|
L(done): |
|
- cntlzd r4,r10 /* Count leading zeroes before c matches. */ |
|
- cntlzd r0,r11 /* Count leading zeroes before null matches. */ |
|
- cmpld cr7,r4,r0 |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ addi r3,r10,-1 |
|
+ andc r3,r3,r10 |
|
+ popcntd r0,r3 |
|
+ addi r4,r11,-1 |
|
+ andc r4,r4,r11 |
|
+ cmpld cr7,r3,r4 |
|
bgt cr7,L(no_match) |
|
- srdi r0,r4,3 /* Convert leading zeroes to bytes. */ |
|
+#else |
|
+ cntlzd r0,r10 /* Count leading zeros before c matches. */ |
|
+ cmpld cr7,r11,r10 |
|
+ bgt cr7,L(no_match) |
|
+#endif |
|
+ srdi r0,r0,3 /* Convert leading zeros to bytes. */ |
|
add r3,r8,r0 /* Return address of the matching c byte |
|
or null in case c was not found. */ |
|
blr |
|
@@ -137,9 +152,13 @@ L(null_match): |
|
|
|
/* Move the doublewords left and right to discard the bits that are |
|
not part of the string and bring them back as zeros. */ |
|
- |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ srd r5,r5,r6 |
|
+ sld r5,r5,r6 |
|
+#else |
|
sld r5,r5,r6 |
|
srd r5,r5,r6 |
|
+#endif |
|
cmpdi cr7,r5,0 /* If r10 == 0, no c or null bytes |
|
have been found. */ |
|
bne cr7,L(done_null) |
|
@@ -194,7 +213,13 @@ L(loop_null): |
|
0xff in the same position as the null byte in the original |
|
doubleword from the string. Use that to calculate the pointer. */ |
|
L(done_null): |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ addi r0,r5,-1 |
|
+ andc r0,r0,r5 |
|
+ popcntd r0,r0 |
|
+#else |
|
cntlzd r0,r5 /* Count leading zeros before the match. */ |
|
+#endif |
|
srdi r0,r0,3 /* Convert leading zeros to bytes. */ |
|
add r3,r8,r0 /* Return address of the matching null byte. */ |
|
blr |
|
Index: glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/power7/strchrnul.S |
|
=================================================================== |
|
--- glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/power7/strchrnul.S.orig |
|
+++ glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/power7/strchrnul.S |
|
@@ -29,8 +29,8 @@ ENTRY (BP_SYM(__strchrnul)) |
|
clrrdi r8,r3,3 /* Align the address to doubleword boundary. */ |
|
|
|
/* Replicate byte to doubleword. */ |
|
- rlwimi r4,r4,8,16,23 |
|
- rlwimi r4,r4,16,0,15 |
|
+ insrdi r4,r4,8,48 |
|
+ insrdi r4,r4,16,32 |
|
insrdi r4,r4,32,0 |
|
|
|
rlwinm r6,r3,3,26,28 /* Calculate padding. */ |
|
@@ -46,10 +46,17 @@ ENTRY (BP_SYM(__strchrnul)) |
|
|
|
/* Move the doublewords left and right to discard the bits that are |
|
not part of the string and to bring them back as zeros. */ |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ srd r10,r10,r6 |
|
+ srd r9,r9,r6 |
|
+ sld r10,r10,r6 |
|
+ sld r9,r9,r6 |
|
+#else |
|
sld r10,r10,r6 |
|
sld r9,r9,r6 |
|
srd r10,r10,r6 |
|
srd r9,r9,r6 |
|
+#endif |
|
or r5,r9,r10 /* OR the results to speed things up. */ |
|
cmpdi cr7,r5,0 /* If r5 == 0, no c or null bytes |
|
have been found. */ |
|
@@ -99,7 +106,7 @@ L(loop): |
|
bne cr6,L(done) |
|
|
|
/* The c/null byte must be in the second doubleword. Adjust the |
|
- address again and move the result of cmpb to r10 so we can calculate |
|
+ address again and move the result of cmpb to r5 so we can calculate |
|
the pointer. */ |
|
mr r5,r10 |
|
addi r8,r8,8 |
|
@@ -108,7 +115,13 @@ L(loop): |
|
0xff in the same position as the c/null byte in the original |
|
doubleword from the string. Use that to calculate the pointer. */ |
|
L(done): |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ addi r0,r5,-1 |
|
+ andc r0,r0,r5 |
|
+ popcntd r0,r0 |
|
+#else |
|
cntlzd r0,r5 /* Count leading zeros before the match. */ |
|
+#endif |
|
srdi r0,r0,3 /* Convert leading zeros to bytes. */ |
|
add r3,r8,r0 /* Return address of matching c/null byte. */ |
|
blr |
|
Index: glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/strchr.S |
|
=================================================================== |
|
--- glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/strchr.S.orig |
|
+++ glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/strchr.S |
|
@@ -50,14 +50,16 @@ ENTRY (BP_SYM (strchr)) |
|
#define rIGN r10 /* number of bits we should ignore in the first word */ |
|
#define rMASK r11 /* mask with the bits to ignore set to 0 */ |
|
#define rTMP3 r12 |
|
+#define rTMP4 rIGN |
|
+#define rTMP5 rMASK |
|
|
|
CHECK_BOUNDS_LOW (rSTR, rTMP1, rTMP2) |
|
STORE_RETURN_BOUNDS (rTMP1, rTMP2) |
|
|
|
dcbt 0,rRTN |
|
- rlwimi rCHR, rCHR, 8, 16, 23 |
|
+ insrdi rCHR, rCHR, 8, 48 |
|
li rMASK, -1 |
|
- rlwimi rCHR, rCHR, 16, 0, 15 |
|
+ insrdi rCHR, rCHR, 16, 32 |
|
rlwinm rIGN, rRTN, 3, 26, 28 |
|
insrdi rCHR, rCHR, 32, 0 |
|
lis rFEFE, -0x101 |
|
@@ -70,53 +72,74 @@ ENTRY (BP_SYM (strchr)) |
|
add rFEFE, rFEFE, rTMP1 |
|
/* Test the first (partial?) word. */ |
|
ld rWORD, 0(rSTR) |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ sld rMASK, rMASK, rIGN |
|
+#else |
|
srd rMASK, rMASK, rIGN |
|
+#endif |
|
orc rWORD, rWORD, rMASK |
|
add rTMP1, rFEFE, rWORD |
|
nor rTMP2, r7F7F, rWORD |
|
- and. rTMP1, rTMP1, rTMP2 |
|
+ and. rTMP4, rTMP1, rTMP2 |
|
xor rTMP3, rCHR, rWORD |
|
orc rTMP3, rTMP3, rMASK |
|
b L(loopentry) |
|
|
|
/* The loop. */ |
|
|
|
-L(loop):ldu rWORD, 8(rSTR) |
|
- and. rTMP1, rTMP1, rTMP2 |
|
+L(loop): |
|
+ ldu rWORD, 8(rSTR) |
|
+ and. rTMP5, rTMP1, rTMP2 |
|
/* Test for 0. */ |
|
- add rTMP1, rFEFE, rWORD |
|
- nor rTMP2, r7F7F, rWORD |
|
+ add rTMP1, rFEFE, rWORD /* x - 0x01010101. */ |
|
+ nor rTMP2, r7F7F, rWORD /* ~(x | 0x7f7f7f7f) == ~x & 0x80808080. */ |
|
bne L(foundit) |
|
- and. rTMP1, rTMP1, rTMP2 |
|
+ and. rTMP4, rTMP1, rTMP2 /* (x - 0x01010101) & ~x & 0x80808080. */ |
|
/* Start test for the bytes we're looking for. */ |
|
xor rTMP3, rCHR, rWORD |
|
L(loopentry): |
|
add rTMP1, rFEFE, rTMP3 |
|
nor rTMP2, r7F7F, rTMP3 |
|
beq L(loop) |
|
+ |
|
/* There is a zero byte in the word, but may also be a matching byte (either |
|
before or after the zero byte). In fact, we may be looking for a |
|
- zero byte, in which case we return a match. We guess that this hasn't |
|
- happened, though. */ |
|
-L(missed): |
|
- and. rTMP1, rTMP1, rTMP2 |
|
+ zero byte, in which case we return a match. */ |
|
+ and. rTMP5, rTMP1, rTMP2 |
|
li rRTN, 0 |
|
STORE_RETURN_VALUE (rSTR) |
|
beqlr |
|
-/* It did happen. Decide which one was first... |
|
- I'm not sure if this is actually faster than a sequence of |
|
- rotates, compares, and branches (we use it anyway because it's shorter). */ |
|
+/* At this point: |
|
+ rTMP5 bytes are 0x80 for each match of c, 0 otherwise. |
|
+ rTMP4 bytes are 0x80 for each match of 0, 0 otherwise. |
|
+ But there may be false matches in the next most significant byte from |
|
+ a true match due to carries. This means we need to recalculate the |
|
+ matches using a longer method for big-endian. */ |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ addi rTMP1, rTMP5, -1 |
|
+ andc rTMP1, rTMP1, rTMP5 |
|
+ cntlzd rCLZB, rTMP1 |
|
+ addi rTMP2, rTMP4, -1 |
|
+ andc rTMP2, rTMP2, rTMP4 |
|
+ cmpld rTMP1, rTMP2 |
|
+ bgtlr |
|
+ subfic rCLZB, rCLZB, 64-7 |
|
+#else |
|
+/* I think we could reduce this by two instructions by keeping the "nor" |
|
+ results from the loop for reuse here. See strlen.S tail. Similarly |
|
+ one instruction could be pruned from L(foundit). */ |
|
and rFEFE, r7F7F, rWORD |
|
- or rMASK, r7F7F, rWORD |
|
+ or rTMP5, r7F7F, rWORD |
|
and rTMP1, r7F7F, rTMP3 |
|
- or rIGN, r7F7F, rTMP3 |
|
+ or rTMP4, r7F7F, rTMP3 |
|
add rFEFE, rFEFE, r7F7F |
|
add rTMP1, rTMP1, r7F7F |
|
- nor rWORD, rMASK, rFEFE |
|
- nor rTMP2, rIGN, rTMP1 |
|
+ nor rWORD, rTMP5, rFEFE |
|
+ nor rTMP2, rTMP4, rTMP1 |
|
+ cntlzd rCLZB, rTMP2 |
|
cmpld rWORD, rTMP2 |
|
bgtlr |
|
- cntlzd rCLZB, rTMP2 |
|
+#endif |
|
srdi rCLZB, rCLZB, 3 |
|
add rRTN, rSTR, rCLZB |
|
CHECK_BOUNDS_HIGH_RTN (rSTR, rTMP2, tdlge) |
|
@@ -124,13 +147,21 @@ L(missed): |
|
blr |
|
|
|
L(foundit): |
|
+#ifdef __LITTLE_ENDIAN__ |
|
+ addi rTMP1, rTMP5, -1 |
|
+ andc rTMP1, rTMP1, rTMP5 |
|
+ cntlzd rCLZB, rTMP1 |
|
+ subfic rCLZB, rCLZB, 64-7-64 |
|
+ sradi rCLZB, rCLZB, 3 |
|
+#else |
|
and rTMP1, r7F7F, rTMP3 |
|
- or rIGN, r7F7F, rTMP3 |
|
+ or rTMP4, r7F7F, rTMP3 |
|
add rTMP1, rTMP1, r7F7F |
|
- nor rTMP2, rIGN, rTMP1 |
|
+ nor rTMP2, rTMP4, rTMP1 |
|
cntlzd rCLZB, rTMP2 |
|
subi rSTR, rSTR, 8 |
|
srdi rCLZB, rCLZB, 3 |
|
+#endif |
|
add rRTN, rSTR, rCLZB |
|
CHECK_BOUNDS_HIGH_RTN (rSTR, rTMP2, tdlge) |
|
STORE_RETURN_VALUE (rSTR)
|
|
|