mirror of
https://github.com/crosstool-ng/crosstool-ng.git
synced 2024-12-22 22:22:23 +00:00
643 lines
20 KiB
Diff
643 lines
20 KiB
Diff
# 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)
|