blob: 016a87aa576f9107befa11f28c8f89d6d61d33d5 [file]
// 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 "textflag.h"
// func IndexByte(b []byte, c byte) int
// input:
// R0: b ptr
// R1: b len
// R2: b cap (unused)
// R3: c byte to search
// return
// R0: result
TEXT ·IndexByte<ABIInternal>(SB),NOSPLIT,$0-40
MOVD R3, R2
B ·IndexByteString<ABIInternal>(SB)
// func IndexByteString(s string, c byte) int
// input:
// R0: s ptr
// R1: s len
// R2: c byte to search
// return
// R0: result
TEXT ·IndexByteString<ABIInternal>(SB),NOSPLIT,$0-32
// Core algorithm:
// We load chunks of data, 16 bytes at a time.
// We compare them with the target byte using
// a vector comparison. The vector comparison
// generates byte mask outputs. We convert the byte
// mask to a nibble mask and move it to an int register.
// The lowest bit index / 4 is the matching lane.
//
// An example (using 8 byte vectors for clarity -
// the real code uses 16 byte vectors):
//
// target: [92 92 92 92 92 92 92 92] repeat of input byte
// data: [3a 92 3c 47 21 03 92 b9] loaded from input string
// equalMask: [00 ff 00 00 00 00 ff 00] comparison (VCMEQ)
// nibbleMask: [f0 00 00 0f .. .. .. ..] narrow to nibbles (VSHRN $4)
// register: 0x0f0000f0 move to int register
// idx: 1 lowest set bit / 4
//
// If there is any match, the int register is nonzero and
// we can read the match index off from it. Otherwise, there
// is no match in those 16 bytes.
//
// For the small-input cases (<16 bytes), we have to be careful
// not to read outside 16-byte aligned chunks because of MTE.
// We use some additional masking to extract only matches that
// are valid for the input data.
#define PTR R0
#define LEN R1
#define TARGET R2
#define BASE R3
#define MASK R4
#define VTARGET V0.B16
// Length 0, nothing to do.
CBZ LEN, fail
// Make vector containing the byte we're searching for in each lane.
VMOV TARGET, VTARGET // [c c c ... c c c]
// Small, need to be extra careful about out of bounds.
CMP $16, LEN
BLT small
// Save original location (for computing result).
MOVD PTR, BASE
// Check low bits of length.
AND $0xf, LEN, R9
CBZ R9, multipleOf16
// Length is not a multiple of 16.
// Check 16 bytes, but then advance less to make length aligned.
VLD1 (PTR), [V2.B16] // load data
VCMEQ VTARGET, V2.B16, V2.B16 // compare each byte against the target byte
VSHRN $4, V2.H8, V2.B8 // compact to one nibble per byte
VMOV V2.D[0], MASK // move to general purpose register
CBNZ MASK, foundStart
ADD R9, PTR
SUB R9, LEN
// Length is a nonzero multiple of 16.
multipleOf16:
TBZ $4, LEN, multipleOf32
VLD1.P (PTR), [V2.B16]
SUB $16, LEN
VCMEQ VTARGET, V2.B16, V2.B16
VSHRN $4, V2.H8, V2.B8
VMOV V2.D[0], MASK
CBNZ MASK, found16
CBZ LEN, fail
// Length is a nonzero multiple of 32.
multipleOf32:
TBZ $5, LEN, multipleOf64
VLD1.P (PTR), [V2.B16, V3.B16] // load data, PTR += 32
SUB $32, LEN
VCMEQ VTARGET, V2.B16, V2.B16
VCMEQ VTARGET, V3.B16, V3.B16
VSHRN $4, V2.H8, V2.B8
VSHRN $4, V3.H8, V3.B8
VMOV V2.D[0], MASK
CBNZ MASK, found32
VMOV V3.D[0], MASK
CBNZ MASK, found16
CBZ LEN, fail
// Length is a nonzero multiple of 64.
multipleOf64:
VLD1.P (PTR), [V2.B16, V3.B16, V4.B16, V5.B16] // load data, PTR += 64
SUB $64, LEN
VCMEQ VTARGET, V2.B16, V2.B16
VCMEQ VTARGET, V3.B16, V3.B16
VCMEQ VTARGET, V4.B16, V4.B16
VCMEQ VTARGET, V5.B16, V5.B16
VORR V2.B16, V3.B16, V10.B16
VORR V4.B16, V5.B16, V11.B16
VORR V10.B16, V11.B16, V10.B16
VADDP V10.D2, V10.D2, V10.D2
VMOV V10.D[0], R10
CBNZ R10, found64 // at least one lane matched
CBNZ LEN, multipleOf64
fail:
MOVD $-1, R0
RET
foundStart:
RBIT MASK, MASK
CLZ MASK, R9 // count trailing zeros
LSR $2, R9, R0 // divide by 4
RET
found16:
SUB $16, PTR // undo .P
// On entry to found0, MASK contains a nonzero nibble bitmask
// of matches starting at PTR.
found0:
SUB BASE, PTR // convert pointer to offset
RBIT MASK, MASK
CLZ MASK, R9 // count trailing zeros
ADD R9>>2, PTR, R0 // add nibble index to offset
RET
found32:
SUB $32, PTR // undo .P
B found0
found64:
SUB $64, PTR // undo .P
VSHRN $4, V2.H8, V2.B8
VMOV V2.D[0], MASK
CBNZ MASK, found0
ADD $16, PTR // redo 1/4 of .P
VSHRN $4, V3.H8, V3.B8
VMOV V3.D[0], MASK
CBNZ MASK, found0
ADD $16, PTR // redo 1/4 of .P
VSHRN $4, V4.H8, V4.B8
VMOV V4.D[0], MASK
CBNZ MASK, found0
ADD $16, PTR // redo 1/4 of .P
VSHRN $4, V5.H8, V5.B8
VMOV V5.D[0], MASK
B found0
// 1-15 bytes
PCALIGN $16
small:
AND $0xf, PTR, R8 // R8 = offset of start in 16-byte region
ADD R8, LEN, R9 // R9 = offset of end (from start's 16-byte region boundary)
MOVD $0, R10 // R10 = low bits of match data to throw away
CMP $16, R9
BGT noAdjust // straddles two 16-byte regions - safe to load directly
// data is all within a single 16-byte region
BIC $0xf, PTR, PTR // round down to start of 16-byte region
LSL $2, R8, R10 // throw away the match bits below original start
noAdjust:
VLD1 (PTR), [V2.B16]
VCMEQ VTARGET, V2.B16, V2.B16
VSHRN $4, V2.H8, V2.B8 // compact to one nibble per byte
VMOV V2.D[0], MASK // move to general purpose register
LSR R10, MASK, MASK // discard matches before string start
RBIT MASK, MASK
CLZ MASK, R9
LSR $2, R9, R9
CMP LEN, R9
CSINV LT, R9, ZR, R0 // if first match past end of string, set return value to -1
RET