2 * Copyright (c) 2012 ARM Ltd
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the company may not be used to endorse or promote
14 * products derived from this software without specific prior written
17 * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define S2LOMEMEQ lsleq
35 #define MSB 0x000000ff
36 #define LSB 0xff000000
37 #define BYTE0_OFFSET 24
38 #define BYTE1_OFFSET 16
39 #define BYTE2_OFFSET 8
40 #define BYTE3_OFFSET 0
41 #else /* not __ARMEB__ */
43 #define S2LOMEMEQ lsreq
45 #define BYTE0_OFFSET 0
46 #define BYTE1_OFFSET 8
47 #define BYTE2_OFFSET 16
48 #define BYTE3_OFFSET 24
49 #define MSB 0xff000000
50 #define LSB 0x000000ff
51 #endif /* not __ARMEB__ */
55 #if defined (__thumb__)
60 .type strcmp, %function
63 #if (defined (__thumb__) && !defined (__thumb2__))
76 #elif (defined (__OPTIMIZE_SIZE__) || defined (PREFER_SIZE_OVER_SPEED))
88 #elif (defined (_ISA_THUMB_2) || defined (_ISA_ARM_6))
89 /* Use LDRD whenever possible. */
91 /* The main thing to look out for when comparing large blocks is that
92 the loads do not cross a page boundary when loading past the index
93 of the byte with the first difference or the first string-terminator.
95 For example, if the strings are identical and the string-terminator
96 is at index k, byte by byte comparison will not load beyond address
97 s1+k and s2+k; word by word comparison may load up to 3 bytes beyond
98 k; double word - up to 7 bytes. If the load of these bytes crosses
99 a page boundary, it might cause a memory fault (if the page is not mapped)
100 that would not have happened in byte by byte comparison.
102 If an address is (double) word aligned, then a load of a (double) word
103 from that address will not cross a page boundary.
104 Therefore, the algorithm below considers word and double-word alignment
105 of strings separately. */
107 /* High-level description of the algorithm.
109 * The fast path: if both strings are double-word aligned,
110 use LDRD to load two words from each string in every loop iteration.
111 * If the strings have the same offset from a word boundary,
112 use LDRB to load and compare byte by byte until
113 the first string is aligned to a word boundary (at most 3 bytes).
114 This is optimized for quick return on short unaligned strings.
115 * If the strings have the same offset from a double-word boundary,
116 use LDRD to load two words from each string in every loop iteration, as in the fast path.
117 * If the strings do not have the same offset from a double-word boundary,
118 load a word from the second string before the loop to initialize the queue.
119 Use LDRD to load two words from every string in every loop iteration.
120 Inside the loop, load the second word from the second string only after comparing
121 the first word, using the queued value, to guarantee safety across page boundaries.
122 * If the strings do not have the same offset from a word boundary,
123 use LDR and a shift queue. Order of loads and comparisons matters,
124 similarly to the previous case.
126 * Use UADD8 and SEL to compare words, and use REV and CLZ to compute the return value.
127 * The only difference between ARM and Thumb modes is the use of CBZ instruction.
128 * The only difference between big and little endian is the use of REV in little endian
129 to compute the return value, instead of MOV.
130 * No preload. [TODO.]
133 .macro m_cbz reg label
136 #else /* not defined __thumb2__ */
139 #endif /* not defined __thumb2__ */
142 .macro m_cbnz reg label
145 #else /* not defined __thumb2__ */
148 #endif /* not defined __thumb2__ */
152 /* Macro to save temporary registers and prepare magic values. */
154 strd r4, r5, [sp, #8]
156 mvn r6, #0 /* all F */
157 mov r7, #0 /* all 0 */
160 .macro magic_compare_and_branch w1 w2 label
161 /* Macro to compare registers w1 and w2 and conditionally branch to label. */
162 cmp \w1, \w2 /* Are w1 and w2 the same? */
163 magic_find_zero_bytes \w1
165 cmpeq ip, #0 /* Is there a zero byte in w1? */
167 .endm /* magic_compare_and_branch */
169 .macro magic_find_zero_bytes w1
170 /* Macro to find all-zero bytes in w1, result is in ip. */
171 #if (defined (__ARM_FEATURE_DSP))
174 #else /* not defined (__ARM_FEATURE_DSP) */
175 /* __ARM_FEATURE_DSP is not defined for some Cortex-M processors.
176 Coincidently, these processors only have Thumb-2 mode, where we can use the
177 the (large) magic constant available directly as an immediate in instructions.
178 Note that we cannot use the magic constant in ARM mode, where we need
179 to create the constant in a register. */
180 sub ip, \w1, #0x01010101
182 and ip, ip, #0x80808080
183 #endif /* not defined (__ARM_FEATURE_DSP) */
184 .endm /* magic_find_zero_bytes */
186 .macro setup_return w1 w2
190 #else /* not __ARMEB__ */
193 #endif /* not __ARMEB__ */
194 .endm /* setup_return */
201 /* Are both strings double-word aligned? */
211 /* Get here when the strings to compare are double-word aligned. */
212 /* Compare two words in every iteration. */
220 /* Load the next double-word from each string. */
221 ldrd r2, r3, [r0], #8
222 ldrd r4, r5, [r1], #8
224 magic_compare_and_branch w1=r2, w2=r4, label=return_24
225 magic_compare_and_branch w1=r3, w2=r5, label=return_35
229 /* Is the first string word-aligned? */
233 /* Fast compare byte by byte until the first string is word-aligned. */
234 /* The offset of r0 from a word boundary is in ip. Thus, the number of bytes
235 to read until the next word boudnary is 4-ip. */
244 uxtb r3, r2, ror #BYTE1_OFFSET
247 m_cbz reg=r3, label=fast_return
251 uxtb r3, r2, ror #BYTE2_OFFSET
254 m_cbz reg=r3, label=fast_return
258 uxtb r3, r2, ror #BYTE3_OFFSET
261 m_cbnz reg=r3, label=word_aligned_r0
269 /* The first string is word-aligned. */
270 /* Is the second string word-aligned? */
275 /* The strings are word-aligned. */
276 /* Is the first string double-word aligned? */
278 beq doubleword_aligned_r0
280 /* If r0 is not double-word aligned yet, align it by loading
281 and comparing the next word from each string. */
284 magic_compare_and_branch w1=r2 w2=r4 label=return_24
286 doubleword_aligned_r0:
287 /* Get here when r0 is double-word aligned. */
288 /* Is r1 doubleword_aligned? */
290 beq doubleword_aligned
292 /* Get here when the strings to compare are word-aligned,
293 r0 is double-word aligned, but r1 is not double-word aligned. */
295 /* Initialize the queue. */
298 /* Compare two words in every iteration. */
306 /* Load the next double-word from each string and compare. */
307 ldrd r2, r3, [r0], #8
308 magic_compare_and_branch w1=r2 w2=r5 label=return_25
309 ldrd r4, r5, [r1], #8
310 magic_compare_and_branch w1=r3 w2=r4 label=return_34
313 .macro miscmp_word offsetlo offsethi
314 /* Macro to compare misaligned strings. */
315 /* r0, r1 are word-aligned, and at least one of the strings
316 is not double-word aligned. */
317 /* Compare one word in every loop iteration. */
318 /* OFFSETLO is the original bit-offset of r1 from a word-boundary,
319 OFFSETHI is 32 - OFFSETLO (i.e., offset from the next word). */
321 /* Initialize the shift queue. */
324 /* Compare one word from each string in every loop iteration. */
328 S2LOMEM r5, r5, #\offsetlo
329 magic_find_zero_bytes w1=r3
330 cmp r7, ip, S2HIMEM #\offsetlo
331 and r2, r3, r6, S2LOMEM #\offsetlo
338 S2HIMEM r2, r5, #\offsethi
343 .endm /* miscmp_word */
346 /* r0 is word-aligned, r1 is at offset ip from a word. */
347 /* Align r1 to the (previous) word-boundary. */
350 /* Unaligned comparison word by word using LDRs. */
352 beq miscmp_word_16 /* If ip == 2. */
353 bge miscmp_word_24 /* If ip == 3. */
354 miscmp_word offsetlo=8 offsethi=24 /* If ip == 1. */
355 miscmp_word_16: miscmp_word offsetlo=16 offsethi=16
356 miscmp_word_24: miscmp_word offsetlo=24 offsethi=8
360 setup_return w1=r3, w2=r2
363 setup_return w1=r3, w2=r4
366 setup_return w1=r2, w2=r5
369 setup_return w1=r3, w2=r5
372 setup_return w1=r2, w2=r4
378 #else /* not __ARMEB__ */
380 #endif /* not __ARMEB__ */
382 /* Restore temporaries early, before computing the return value. */
384 ldrd r4, r5, [sp, #8]
387 /* There is a zero or a different byte between r1 and r2. */
388 /* r0 contains a mask of all-zero bytes in r1. */
389 /* Using r0 and not ip here because cbz requires low register. */
390 m_cbz reg=r0, label=compute_return_value
392 /* r0 contains the number of bits on the left of the first all-zero byte in r1. */
394 /* Here, r0 contains the number of bits on the right of the first all-zero byte in r1. */
398 compute_return_value:
401 /* The return value is computed as follows.
402 If r1>r2 then (C==1 and Z==0) and LS doesn't hold and r0 is #1 at return.
403 If r1<r2 then (C==0 and Z==0) and we execute SBC with carry_in=0,
404 which means r0:=r0-r0-1 and r0 is #-1 at return.
405 If r1=r2 then (C==1 and Z==1) and we execute SBC with carry_in=1,
406 which means r0:=r0-r0 and r0 is #0 at return.
407 (C==0 and Z==1) cannot happen because the carry bit is "not borrow". */
413 #else /* !(defined (_ISA_THUMB_2) || defined (_ISA_ARM_6)
414 defined (__OPTIMIZE_SIZE__) || defined (PREFER_SIZE_OVER_SPEED) ||
415 (defined (__thumb__) && !defined (__thumb2__))) */
417 /* Use LDR whenever possible. */
420 #define magic1(REG) 0x01010101
421 #define magic2(REG) 0x80808080
423 #define magic1(REG) REG
424 #define magic2(REG) REG, lsl #7
431 /* Strings not at same byte offset from a word boundary. */
440 /* Although s1 and s2 have identical initial alignment, they are
441 not currently word aligned. Rather than comparing bytes,
442 make sure that any bytes fetched from before the addressed
443 bytes are forced to 0xff. Then they will always compare
454 /* Load the 'magic' constant 0x01010101. */
457 orr r4, r4, r4, lsl #8
458 orr r4, r4, r4, lsl #16
464 sub r2, ip, magic1(r4)
467 /* check for any zero bytes in first word */
474 /* There's a zero or a different byte in the word */
479 cmpcs r0, r3, S2HIMEM #24
483 /* On a big-endian machine, r0 contains the desired byte in bits
484 0-7; on a little-endian machine they are in bits 24-31. In
485 both cases the other bits in r0 are all zero. For r3 the
486 interesting byte is at the other end of the word, but the
487 other bits are not necessarily zero. We need a signed result
488 representing the differnece in the unsigned bytes, so for the
489 little-endian case we can't just shift the interesting bits
492 sub r0, r0, r3, lsr #24
496 /* No RSB instruction in Thumb2 */
500 rsb r0, r3, r0, lsr #24
512 /* The assembly code below is based on the following alogrithm. */
521 #define body(shift) \
522 mask = 0xffffffffU RSHIFT shift; \
528 if (__builtin_expect(t1 != w2 RSHIFT shift, 0)) \
533 if (__builtin_expect(((w1 - b1) & ~w1) & (b1 << 7), 0)) \
535 /* See comment in assembler below re syndrome on big-endian */\
536 if ((((w1 - b1) & ~w1) & (b1 << 7)) & mask) \
541 t1 = w1 RSHIFT (32 - shift); \
542 w2 = (w2 LSHIFT (32 - shift)) RSHIFT (32 - shift); \
548 if (__builtin_expect(t1 != w2 LSHIFT (32 - shift), 0)) \
550 t1 = w1 >> (32 - shift); \
551 w2 = (w2 << (32 - shift)) RSHIFT (32 - shift); \
562 unsigned b1 = 0x01010101;
566 while (((unsigned) s1) & 3)
570 if (c1 == 0 || c1 != c2)
573 wp1 = (unsigned*) (((unsigned)s1) & ~3);
574 wp2 = (unsigned*) (((unsigned)s2) & ~3);
575 t1 = ((unsigned) s2) & 3;
592 c1 = (char) t1 >> 24;
593 c2 = (char) w2 >> 24;
594 #else /* not __ARMEB__ */
597 #endif /* not __ARMEB__ */
600 } while (c1 != 0 && c1 == c2);
613 /* First of all, compare bytes until wp1(sp1) is word-aligned. */
629 //stmfd sp!, {r4, r5}
631 orr b1, b1, b1, lsl #8
632 orr b1, b1, b1, lsl #16
642 /* Critical inner Loop: Block with 3 bytes initial overlap */
646 cmp t1, w2, S2LOMEM #8
650 ands r3, r3, b1, lsl #7
655 cmp t1, w2, S2HIMEM #24
665 /* The syndrome value may contain false ones if the string ends
666 with the bytes 0x01 0x00 */
669 tstne w1, #0x00ff0000
670 tstne w1, #0x0000ff00
673 bics r3, r3, #0xff000000
688 /* Critical inner Loop: Block with 2 bytes initial overlap */
695 cmp t1, w2, S2LOMEM #16
697 ands r3, r3, b1, lsl #7
702 cmp t1, w2, S2HIMEM #16
709 /* The syndrome value may contain false ones if the string ends
710 with the bytes 0x01 0x00 */
713 tstne w1, #0x00ff0000
733 /* Critical inner Loop: Block with 1 byte initial overlap */
737 cmp t1, w2, S2LOMEM #24
741 ands r3, r3, b1, lsl #7
746 cmp t1, w2, S2HIMEM #8
754 /* The syndrome value may contain false ones if the string ends
755 with the bytes 0x01 0x00 */
765 //ldmfd sp!, {r4, r5}
780 //ldmfd sp!, {r4, r5}
785 #endif /* !(defined (_ISA_THUMB_2) || defined (_ISA_ARM_6)
786 defined (__OPTIMIZE_SIZE__) || defined (PREFER_SIZE_OVER_SPEED) ||
787 (defined (__thumb__) && !defined (__thumb2__))) */