| // 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. |
| |
| //go:build !plan9 |
| |
| #include "go_asm.h" |
| #include "textflag.h" |
| |
| TEXT ·IndexByte(SB), NOSPLIT, $0-40 |
| MOVQ b_base+0(FP), SI |
| MOVQ b_len+8(FP), BX |
| MOVB c+24(FP), AL |
| LEAQ ret+32(FP), R8 |
| JMP indexbytebody<>(SB) |
| |
| TEXT ·IndexByteString(SB), NOSPLIT, $0-32 |
| MOVQ s_base+0(FP), SI |
| MOVQ s_len+8(FP), BX |
| MOVB c+16(FP), AL |
| LEAQ ret+24(FP), R8 |
| JMP indexbytebody<>(SB) |
| |
| // input: |
| // SI: data |
| // BX: data len |
| // AL: byte sought |
| // R8: address to put result |
| TEXT indexbytebody<>(SB), NOSPLIT, $0 |
| // Shuffle X0 around so that each byte contains |
| // the character we're looking for. |
| MOVD AX, X0 |
| PUNPCKLBW X0, X0 |
| PUNPCKLBW X0, X0 |
| PSHUFL $0, X0, X0 |
| |
| CMPQ BX, $16 |
| JLT small |
| |
| MOVQ SI, DI |
| |
| CMPQ BX, $32 |
| JA avx2 |
| sse: |
| LEAQ -16(SI)(BX*1), AX // AX = address of last 16 bytes |
| JMP sseloopentry |
| |
| PCALIGN $16 |
| sseloop: |
| // Move the next 16-byte chunk of the data into X1. |
| MOVOU (DI), X1 |
| // Compare bytes in X0 to X1. |
| PCMPEQB X0, X1 |
| // Take the top bit of each byte in X1 and put the result in DX. |
| PMOVMSKB X1, DX |
| // Find first set bit, if any. |
| BSFL DX, DX |
| JNZ ssesuccess |
| // Advance to next block. |
| ADDQ $16, DI |
| sseloopentry: |
| CMPQ DI, AX |
| JB sseloop |
| |
| // Search the last 16-byte chunk. This chunk may overlap with the |
| // chunks we've already searched, but that's ok. |
| MOVQ AX, DI |
| MOVOU (AX), X1 |
| PCMPEQB X0, X1 |
| PMOVMSKB X1, DX |
| BSFL DX, DX |
| JNZ ssesuccess |
| |
| failure: |
| MOVQ $-1, (R8) |
| RET |
| |
| // We've found a chunk containing the byte. |
| // The chunk was loaded from DI. |
| // The index of the matching byte in the chunk is DX. |
| // The start of the data is SI. |
| ssesuccess: |
| SUBQ SI, DI // Compute offset of chunk within data. |
| ADDQ DX, DI // Add offset of byte within chunk. |
| MOVQ DI, (R8) |
| RET |
| |
| // handle for lengths < 16 |
| small: |
| TESTQ BX, BX |
| JEQ failure |
| |
| // Check if we'll load across a page boundary. |
| LEAQ 16(SI), AX |
| TESTW $0xff0, AX |
| JEQ endofpage |
| |
| MOVOU (SI), X1 // Load data |
| PCMPEQB X0, X1 // Compare target byte with each byte in data. |
| PMOVMSKB X1, DX // Move result bits to integer register. |
| BSFL DX, DX // Find first set bit. |
| JZ failure // No set bit, failure. |
| CMPL DX, BX |
| JAE failure // Match is past end of data. |
| MOVQ DX, (R8) |
| RET |
| |
| endofpage: |
| MOVOU -16(SI)(BX*1), X1 // Load data into the high end of X1. |
| PCMPEQB X0, X1 // Compare target byte with each byte in data. |
| PMOVMSKB X1, DX // Move result bits to integer register. |
| MOVL BX, CX |
| SHLL CX, DX |
| SHRL $16, DX // Shift desired bits down to bottom of register. |
| BSFL DX, DX // Find first set bit. |
| JZ failure // No set bit, failure. |
| MOVQ DX, (R8) |
| RET |
| |
| avx2: |
| #ifndef hasAVX2 |
| CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 |
| JNE sse |
| #endif |
| MOVD AX, X0 |
| LEAQ -32(SI)(BX*1), R11 |
| VPBROADCASTB X0, Y1 |
| |
| PCALIGN $32 |
| avx2_loop: |
| VMOVDQU (DI), Y2 |
| VPCMPEQB Y1, Y2, Y3 |
| VPTEST Y3, Y3 |
| JNZ avx2success |
| ADDQ $32, DI |
| CMPQ DI, R11 |
| JLT avx2_loop |
| MOVQ R11, DI |
| VMOVDQU (DI), Y2 |
| VPCMPEQB Y1, Y2, Y3 |
| VPTEST Y3, Y3 |
| JNZ avx2success |
| VZEROUPPER |
| MOVQ $-1, (R8) |
| RET |
| |
| avx2success: |
| VPMOVMSKB Y3, DX |
| BSFL DX, DX |
| SUBQ SI, DI |
| ADDQ DI, DX |
| MOVQ DX, (R8) |
| VZEROUPPER |
| RET |