1/* Optimized strchr implementation for PowerPC32/POWER7 using cmpb insn. 2 Copyright (C) 2010-2022 Free Software Foundation, Inc. 3 This file is part of the GNU C Library. 4 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. 9 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. 14 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/>. */ 18 19#include <sysdep.h> 20 21/* int [r3] strchr (char *s [r3], int c [r4]) */ 22 .machine power7 23ENTRY (strchr) 24 CALL_MCOUNT 25 dcbt 0,r3 26 clrrwi r8,r3,2 /* Align the address to word boundary. */ 27 cmpwi cr7,r4,0 28 lwz r12,0(r8) /* Load word from memory. */ 29 li r0,0 /* Word with null chars to use 30 with cmpb. */ 31 32 rlwinm r6,r3,3,27,28 /* Calculate padding. */ 33 34 beq cr7,L(null_match) 35 36 /* Replicate byte to word. */ 37 insrwi r4,r4,8,16 38 insrwi r4,r4,16,0 39 40 /* Now r4 has a word of c bytes and r0 has 41 a word of null bytes. */ 42 43 cmpb r10,r12,r4 /* Compare each byte against c byte. */ 44 cmpb r11,r12,r0 /* Compare each byte against null byte. */ 45 46 /* Move the words left and right to discard the bits that are 47 not part of the string and to bring them back as zeros. */ 48#ifdef __LITTLE_ENDIAN__ 49 srw r10,r10,r6 50 srw r11,r11,r6 51 slw r10,r10,r6 52 slw r11,r11,r6 53#else 54 slw r10,r10,r6 55 slw r11,r11,r6 56 srw r10,r10,r6 57 srw r11,r11,r6 58#endif 59 or r5,r10,r11 /* OR the results to speed things up. */ 60 cmpwi cr7,r5,0 /* If r5 == 0, no c or null bytes 61 have been found. */ 62 bne cr7,L(done) 63 64 mtcrf 0x01,r8 65 66 /* Are we now aligned to a doubleword boundary? If so, skip to 67 the main loop. Otherwise, go through the alignment code. */ 68 69 bt 29,L(loop) 70 71 /* Handle WORD2 of pair. */ 72 lwzu r12,4(r8) 73 cmpb r10,r12,r4 74 cmpb r11,r12,r0 75 or r5,r10,r11 76 cmpwi cr7,r5,0 77 bne cr7,L(done) 78 b L(loop) /* We branch here (rather than falling through) 79 to skip the nops due to heavy alignment 80 of the loop below. */ 81 82 .p2align 5 83L(loop): 84 /* Load two words, compare and merge in a 85 single register for speed. This is an attempt 86 to speed up the null-checking process for bigger strings. */ 87 lwz r12,4(r8) 88 lwzu r9,8(r8) 89 cmpb r10,r12,r4 90 cmpb r11,r12,r0 91 cmpb r6,r9,r4 92 cmpb r7,r9,r0 93 or r12,r10,r11 94 or r9,r6,r7 95 or r5,r12,r9 96 cmpwi cr7,r5,0 97 beq cr7,L(loop) 98 99 /* OK, one (or both) of the words contains a c/null byte. Check 100 the first word and decrement the address in case the first 101 word really contains a c/null byte. */ 102 103 cmpwi cr6,r12,0 104 addi r8,r8,-4 105 bne cr6,L(done) 106 107 /* The c/null byte must be in the second word. Adjust the address 108 again and move the result of cmpb to r10/r11 so we can calculate 109 the pointer. */ 110 111 mr r10,r6 112 mr r11,r7 113 addi r8,r8,4 114 115 /* r10/r11 have the output of the cmpb instructions, that is, 116 0xff in the same position as the c/null byte in the original 117 word from the string. Use that to calculate the pointer. */ 118L(done): 119#ifdef __LITTLE_ENDIAN__ 120 addi r3,r10,-1 121 andc r3,r3,r10 122 popcntw r0,r3 123 addi r4,r11,-1 124 andc r4,r4,r11 125 cmplw cr7,r3,r4 126 bgt cr7,L(no_match) 127#else 128 cntlzw r0,r10 /* Count leading zeros before c matches. */ 129 cmplw cr7,r11,r10 130 bgt cr7,L(no_match) 131#endif 132 srwi r0,r0,3 /* Convert leading zeros to bytes. */ 133 add r3,r8,r0 /* Return address of the matching c byte 134 or null in case c was not found. */ 135 blr 136 137 .align 4 138L(no_match): 139 li r3,0 140 blr 141 142/* We are here because strchr was called with a null byte. */ 143 .align 4 144L(null_match): 145 /* r0 has a word of null bytes. */ 146 147 cmpb r5,r12,r0 /* Compare each byte against null bytes. */ 148 149 /* Move the words left and right to discard the bits that are 150 not part of the string and bring them back as zeros. */ 151#ifdef __LITTLE_ENDIAN__ 152 srw r5,r5,r6 153 slw r5,r5,r6 154#else 155 slw r5,r5,r6 156 srw r5,r5,r6 157#endif 158 cmpwi cr7,r5,0 /* If r10 == 0, no c or null bytes 159 have been found. */ 160 bne cr7,L(done_null) 161 162 mtcrf 0x01,r8 163 164 /* Are we now aligned to a doubleword boundary? If so, skip to 165 the main loop. Otherwise, go through the alignment code. */ 166 167 bt 29,L(loop_null) 168 169 /* Handle WORD2 of pair. */ 170 lwzu r12,4(r8) 171 cmpb r5,r12,r0 172 cmpwi cr7,r5,0 173 bne cr7,L(done_null) 174 b L(loop_null) /* We branch here (rather than falling through) 175 to skip the nops due to heavy alignment 176 of the loop below. */ 177 178 /* Main loop to look for the end of the string. Since it's a 179 small loop (< 8 instructions), align it to 32-bytes. */ 180 .p2align 5 181L(loop_null): 182 /* Load two words, compare and merge in a 183 single register for speed. This is an attempt 184 to speed up the null-checking process for bigger strings. */ 185 lwz r12,4(r8) 186 lwzu r11,8(r8) 187 cmpb r5,r12,r0 188 cmpb r10,r11,r0 189 or r6,r5,r10 190 cmpwi cr7,r6,0 191 beq cr7,L(loop_null) 192 193 /* OK, one (or both) of the words contains a null byte. Check 194 the first word and decrement the address in case the first 195 word really contains a null byte. */ 196 197 cmpwi cr6,r5,0 198 addi r8,r8,-4 199 bne cr6,L(done_null) 200 201 /* The null byte must be in the second word. Adjust the address 202 again and move the result of cmpb to r10 so we can calculate the 203 pointer. */ 204 205 mr r5,r10 206 addi r8,r8,4 207 208 /* r5 has the output of the cmpb instruction, that is, it contains 209 0xff in the same position as the null byte in the original 210 word from the string. Use that to calculate the pointer. */ 211L(done_null): 212#ifdef __LITTLE_ENDIAN__ 213 addi r0,r5,-1 214 andc r0,r0,r5 215 popcntw r0,r0 216#else 217 cntlzw r0,r5 /* Count leading zeros before the match. */ 218#endif 219 srwi r0,r0,3 /* Convert leading zeros to bytes. */ 220 add r3,r8,r0 /* Return address of the matching null byte. */ 221 blr 222END (strchr) 223weak_alias (strchr, index) 224libc_hidden_builtin_def (strchr) 225