| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| #include "go_asm.h" |
| #include "textflag.h" |
| |
| TEXT ·Compare<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-56 |
| // R0 = a_base (want in R0) |
| // R1 = a_len (want in R1) |
| // R2 = a_cap (unused) |
| // R3 = b_base (want in R2) |
| // R4 = b_len (want in R3) |
| // R5 = b_cap (unused) |
| MOVD R3, R2 |
| MOVD R4, R3 |
| B cmpbody<>(SB) |
| |
| TEXT runtime·cmpstring<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-40 |
| // R0 = a_base |
| // R1 = a_len |
| // R2 = b_base |
| // R3 = b_len |
| B cmpbody<>(SB) |
| |
| // On entry: |
| // R0 points to the start of a |
| // R1 is the length of a |
| // R2 points to the start of b |
| // R3 is the length of b |
| // |
| // On exit: |
| // R0 is the result |
| // R4, R5, R6, R8, R9, R10 and V0-V11 are clobbered |
| TEXT cmpbody<>(SB),NOSPLIT|NOFRAME,$0-0 |
| CMP R0, R2 |
| BEQ samebytes // same starting pointers; compare lengths |
| CMP R1, R3 |
| CSEL LT, R3, R1, R6 // R6 is min(R1, R3) |
| |
| CBZ R6, samebytes |
| BIC $0x3f, R6, R10 |
| CBZ R10, less_than_64 // length < 64, use scalar path |
| ADD R0, R10 // end of 64-byte chunks |
| |
| // Process 64 bytes per iteration using NEON. |
| // Compare 4x16-byte chunks and reduce the result to a single byte. |
| // On mismatch, rewind and fall back to the scalar path. |
| PCALIGN $16 |
| chunk64_loop: |
| VLD1.P (R0), [V0.D2, V1.D2, V2.D2, V3.D2] |
| VLD1.P (R2), [V4.D2, V5.D2, V6.D2, V7.D2] |
| VCMEQ V0.B16, V4.B16, V8.B16 |
| VCMEQ V1.B16, V5.B16, V9.B16 |
| VCMEQ V2.B16, V6.B16, V10.B16 |
| VCMEQ V3.B16, V7.B16, V11.B16 |
| VAND V8.B16, V9.B16, V8.B16 |
| VAND V10.B16, V11.B16, V10.B16 |
| VAND V8.B16, V10.B16, V8.B16 |
| VUMINV V8.B16, V8 |
| VMOV V8.B[0], R4 |
| CBZ R4, neon_mismatch |
| CMP R10, R0 |
| BNE chunk64_loop |
| |
| AND $0x3f, R6, R6 // remaining 0-63 bytes |
| CBZ R6, samebytes |
| |
| less_than_64: |
| // Scalar 16-byte loop for up to 63 bytes, then byte-level tail. |
| BIC $0xf, R6, R10 |
| CBZ R10, small // length < 16 |
| ADD R0, R10 |
| PCALIGN $16 |
| chunk16_loop: |
| LDP.P 16(R0), (R4, R8) |
| LDP.P 16(R2), (R5, R9) |
| CMP R4, R5 |
| BNE cmp |
| CMP R8, R9 |
| BNE cmpnext |
| CMP R10, R0 |
| BNE chunk16_loop |
| AND $0xf, R6, R6 |
| CBZ R6, samebytes |
| SUBS $8, R6 |
| BLT tail |
| // the length of tail > 8 bytes |
| MOVD.P 8(R0), R4 |
| MOVD.P 8(R2), R5 |
| CMP R4, R5 |
| BNE cmp |
| SUB $8, R6 |
| // compare last 8 bytes |
| // tail always reads the final 8-byte window at R0+R6. R6 may be |
| // negative here, overlapping already-verified bytes, which is harmless. |
| tail: |
| MOVD (R0)(R6), R4 |
| MOVD (R2)(R6), R5 |
| CMP R4, R5 |
| BEQ samebytes |
| cmp: |
| REV R4, R4 |
| REV R5, R5 |
| CMP R4, R5 |
| ret: |
| MOVD $1, R0 |
| CNEG HI, R0, R0 |
| RET |
| |
| neon_mismatch: |
| // A mismatch was found in the last 64-byte NEON chunk. Rewind both |
| // pointers and let the scalar path locate the first differing byte. |
| SUB $64, R0 |
| SUB $64, R2 |
| MOVD $64, R6 |
| B less_than_64 |
| |
| small: |
| TBZ $3, R6, lt_8 |
| MOVD (R0), R4 |
| MOVD (R2), R5 |
| CMP R4, R5 |
| BNE cmp |
| SUBS $8, R6 |
| BEQ samebytes |
| B tail |
| lt_8: |
| TBZ $2, R6, lt_4 |
| MOVWU (R0), R4 |
| MOVWU (R2), R5 |
| CMPW R4, R5 |
| BNE cmp |
| SUBS $4, R6 |
| BEQ samebytes |
| ADD $4, R0 |
| ADD $4, R2 |
| lt_4: |
| TBZ $1, R6, lt_2 |
| MOVHU (R0), R4 |
| MOVHU (R2), R5 |
| CMPW R4, R5 |
| BNE cmp |
| ADD $2, R0 |
| ADD $2, R2 |
| lt_2: |
| TBZ $0, R6, samebytes |
| one: |
| MOVBU (R0), R4 |
| MOVBU (R2), R5 |
| CMPW R4, R5 |
| BNE ret |
| samebytes: |
| CMP R3, R1 |
| CSET NE, R0 |
| CNEG LO, R0, R0 |
| RET |
| cmpnext: |
| REV R8, R4 |
| REV R9, R5 |
| CMP R4, R5 |
| B ret |