// Code generated by command: go run valid_asm.go -pkg utf8 -out ../utf8/valid_amd64.s -stubs ../utf8/valid_amd64.go. DO NOT EDIT.
//go:build !purego
#include "textflag.h"
// func validateAvx(p []byte) byte
// Requires: AVX, AVX2
TEXT ·validateAvx(SB), NOSPLIT, $0-25
MOVQ p_base+0(FP), AX
MOVQ p_len+8(FP), CX
MOVB $0x01, DL
// Prepare the constant masks
VMOVDQU incomplete_mask<>+0(SB), Y0
VMOVDQU cont4_vec<>+0(SB), Y1
VMOVDQU cont3_vec<>+0(SB), Y2
// High nibble of current byte
VMOVDQU nibble1_errors<>+0(SB), Y3
// Low nibble of current byte
VMOVDQU nibble2_errors<>+0(SB), Y4
// High nibble of the next byte
VMOVDQU nibble3_errors<>+0(SB), Y5
// Nibble mask
VMOVDQU nibble_mask<>+0(SB), Y6
// MSB mask
VMOVDQU msb_mask<>+0(SB), Y7
// For the first pass, set the previous block as zero.
VXORPS Y8, Y8, Y8
// Zeroes the error vector.
VXORPS Y9, Y9, Y9
// Zeroes the "previous block was incomplete" vector.
VXORPS Y10, Y10, Y10
// Top of the loop.
check_input:
// if bytes left >= 32
CMPQ CX, $0x20
// go process the next block
JGE process
// If < 32 bytes left
// Fast exit if done
CMPQ CX, $0x00
JE end
// If 0 < bytes left < 32
VPXOR Y12, Y12, Y12
MOVQ $0x0000000000000020, BX
SUBQ CX, BX
SUBQ BX, AX
VMOVDQU (AX), Y11
CMPQ CX, $0x10
JA tail_load_large
// Shift right that works if remaining bytes <= 16, safe next to a page boundary
VPERM2I128 $0x03, Y11, Y12, Y11
LEAQ shuffle_clear_mask<>+16(SB), SI
ADDQ CX, BX
ADDQ CX, BX
SUBQ $0x20, BX
SUBQ BX, SI
VMOVDQU (SI), Y13
VPSHUFB Y13, Y11, Y11
XORQ CX, CX
JMP loaded
// Shift right that works if remaining bytes >= 16, safe next to a page boundary
tail_load_large:
ADDQ CX, BX
ADDQ CX, BX
SUBQ $0x30, BX
LEAQ shuffle_mask<>+16(SB), SI
SUBQ BX, SI
VMOVDQU (SI), Y13
VPSHUFB Y13, Y11, Y14
VPERM2I128 $0x03, Y11, Y12, Y11
VPSHUFB Y13, Y11, Y11
LEAQ blend_mask<>+16(SB), CX
SUBQ BX, CX
VBROADCASTF128 (CX), Y12
VPBLENDVB Y12, Y14, Y11, Y11
XORQ CX, CX
JMP loaded
// Process one 32B block of data
process:
// Load the next block of bytes
VMOVDQU (AX), Y11
SUBQ $0x20, CX
ADDQ $0x20, AX
loaded:
// Fast check to see if ASCII
VPMOVMSKB Y11, BX
CMPL BX, $0x00
JNZ non_ascii
// If this whole block is ASCII, there is nothing to do, and it is an error if any of the previous code point was incomplete.
VPOR Y9, Y10, Y9
JMP check_input
non_ascii:
XORB DL, DL
// Prepare intermediate vector for push operations
VPERM2I128 $0x03, Y8, Y11, Y8
// Check errors on the high nibble of the previous byte
VPALIGNR $0x0f, Y8, Y11, Y10
VPSRLW $0x04, Y10, Y12
VPAND Y12, Y6, Y12
VPSHUFB Y12, Y3, Y12
// Check errors on the low nibble of the previous byte
VPAND Y10, Y6, Y10
VPSHUFB Y10, Y4, Y10
VPAND Y10, Y12, Y12
// Check errors on the high nibble on the current byte
VPSRLW $0x04, Y11, Y10
VPAND Y10, Y6, Y10
VPSHUFB Y10, Y5, Y10
VPAND Y10, Y12, Y12
// Find 3 bytes continuations
VPALIGNR $0x0e, Y8, Y11, Y10
VPSUBUSB Y2, Y10, Y10
// Find 4 bytes continuations
VPALIGNR $0x0d, Y8, Y11, Y8
VPSUBUSB Y1, Y8, Y8
// Combine them to have all continuations
VPOR Y10, Y8, Y8
// Perform a byte-sized signed comparison with zero to turn any non-zero bytes into 0xFF.
VXORPS Y10, Y10, Y10
VPCMPGTB Y10, Y8, Y8
// Find bytes that are continuations by looking at their most significant bit.
VPAND Y7, Y8, Y8
// Find mismatches between expected and actual continuation bytes
VPXOR Y8, Y12, Y8
// Store result in sticky error
VPOR Y9, Y8, Y9
// Prepare for next iteration
VPSUBUSB Y0, Y11, Y10
VMOVDQU Y11, Y8
// End of loop
JMP check_input
end:
// If the previous block was incomplete, this is an error.
VPOR Y10, Y9, Y9
// Return whether any error bit was set
VPTEST Y9, Y9
SETEQ AL
// Bit 0 tells if the input is valid utf8, bit 1 tells if it's valid ascii
ANDB AL, DL
SHLB $0x01, DL
ORB DL, AL
MOVB AL, ret+24(FP)
VZEROUPPER
RET
DATA incomplete_mask<>+0(SB)/8, $0xffffffffffffffff
DATA incomplete_mask<>+8(SB)/8, $0xffffffffffffffff
DATA incomplete_mask<>+16(SB)/8, $0xffffffffffffffff
DATA incomplete_mask<>+24(SB)/8, $0xbfdfefffffffffff
GLOBL incomplete_mask<>(SB), RODATA|NOPTR, $32
DATA cont4_vec<>+0(SB)/8, $0xefefefefefefefef
DATA cont4_vec<>+8(SB)/8, $0xefefefefefefefef
DATA cont4_vec<>+16(SB)/8, $0xefefefefefefefef
DATA cont4_vec<>+24(SB)/8, $0xefefefefefefefef
GLOBL cont4_vec<>(SB), RODATA|NOPTR, $32
DATA cont3_vec<>+0(SB)/8, $0xdfdfdfdfdfdfdfdf
DATA cont3_vec<>+8(SB)/8, $0xdfdfdfdfdfdfdfdf
DATA cont3_vec<>+16(SB)/8, $0xdfdfdfdfdfdfdfdf
DATA cont3_vec<>+24(SB)/8, $0xdfdfdfdfdfdfdfdf
GLOBL cont3_vec<>(SB), RODATA|NOPTR, $32
DATA nibble1_errors<>+0(SB)/8, $0x0202020202020202
DATA nibble1_errors<>+8(SB)/8, $0x4915012180808080
DATA nibble1_errors<>+16(SB)/8, $0x0202020202020202
DATA nibble1_errors<>+24(SB)/8, $0x4915012180808080
GLOBL nibble1_errors<>(SB), RODATA|NOPTR, $32
DATA nibble2_errors<>+0(SB)/8, $0xcbcbcb8b8383a3e7
DATA nibble2_errors<>+8(SB)/8, $0xcbcbdbcbcbcbcbcb
DATA nibble2_errors<>+16(SB)/8, $0xcbcbcb8b8383a3e7
DATA nibble2_errors<>+24(SB)/8, $0xcbcbdbcbcbcbcbcb
GLOBL nibble2_errors<>(SB), RODATA|NOPTR, $32
DATA nibble3_errors<>+0(SB)/8, $0x0101010101010101
DATA nibble3_errors<>+8(SB)/8, $0x01010101babaaee6
DATA nibble3_errors<>+16(SB)/8, $0x0101010101010101
DATA nibble3_errors<>+24(SB)/8, $0x01010101babaaee6
GLOBL nibble3_errors<>(SB), RODATA|NOPTR, $32
DATA nibble_mask<>+0(SB)/8, $0x0f0f0f0f0f0f0f0f
DATA nibble_mask<>+8(SB)/8, $0x0f0f0f0f0f0f0f0f
DATA nibble_mask<>+16(SB)/8, $0x0f0f0f0f0f0f0f0f
DATA nibble_mask<>+24(SB)/8, $0x0f0f0f0f0f0f0f0f
GLOBL nibble_mask<>(SB), RODATA|NOPTR, $32
DATA msb_mask<>+0(SB)/8, $0x8080808080808080
DATA msb_mask<>+8(SB)/8, $0x8080808080808080
DATA msb_mask<>+16(SB)/8, $0x8080808080808080
DATA msb_mask<>+24(SB)/8, $0x8080808080808080
GLOBL msb_mask<>(SB), RODATA|NOPTR, $32
DATA shuffle_mask<>+0(SB)/8, $0x0706050403020100
DATA shuffle_mask<>+8(SB)/8, $0x0f0e0d0c0b0a0908
DATA shuffle_mask<>+16(SB)/8, $0x0706050403020100
DATA shuffle_mask<>+24(SB)/8, $0x0f0e0d0c0b0a0908
DATA shuffle_mask<>+32(SB)/8, $0x0706050403020100
DATA shuffle_mask<>+40(SB)/8, $0x0f0e0d0c0b0a0908
GLOBL shuffle_mask<>(SB), RODATA|NOPTR, $48
DATA shuffle_clear_mask<>+0(SB)/8, $0x0706050403020100
DATA shuffle_clear_mask<>+8(SB)/8, $0x0f0e0d0c0b0a0908
DATA shuffle_clear_mask<>+16(SB)/8, $0xffffffffffffffff
DATA shuffle_clear_mask<>+24(SB)/8, $0xffffffffffffffff
DATA shuffle_clear_mask<>+32(SB)/8, $0xffffffffffffffff
DATA shuffle_clear_mask<>+40(SB)/8, $0xffffffffffffffff
GLOBL shuffle_clear_mask<>(SB), RODATA|NOPTR, $48
DATA blend_mask<>+0(SB)/8, $0xffffffffffffffff
DATA blend_mask<>+8(SB)/8, $0xffffffffffffffff
DATA blend_mask<>+16(SB)/8, $0x0000000000000000
DATA blend_mask<>+24(SB)/8, $0x0000000000000000
DATA blend_mask<>+32(SB)/8, $0xffffffffffffffff
DATA blend_mask<>+40(SB)/8, $0xffffffffffffffff
GLOBL blend_mask<>(SB), RODATA|NOPTR, $48