// File: Alcaro[P]bridges/arlib/memmem.cpp
// Function: memmem_sse42(unsigned char const*, unsigned long, unsigned char const*, unsigned long)
__attribute__((target("sse4.2")))
static const uint8_t * memmem_sse42(const uint8_t * haystack, size_t haystacklen, const uint8_t * needle, size_t needlelen)
{
	__m128i needle_sse = load_sse2_small_highundef(needle, needlelen);
	
	__m128i firstbyte = needle_sse;
	firstbyte = _mm_unpacklo_epi8(firstbyte, firstbyte); // duplicate bottom byte, ignore upper 14
	firstbyte = _mm_unpacklo_epi16(firstbyte, firstbyte); // duplicate bottom two bytes, ignore upper 12
	firstbyte = _mm_shuffle_epi32(firstbyte, _MM_SHUFFLE(0,0,0,0)); // and duplicate bottom four to all 16
	
	size_t step = 16+1-needlelen;
	
#define CMPSTR_FLAGS (_SIDD_UBYTE_OPS|_SIDD_CMP_EQUAL_ORDERED|_SIDD_LEAST_SIGNIFICANT)
	while (haystacklen >= 16)
	{
		__m128i haystack_sse = _mm_loadu_si128((__m128i*)haystack);
		if (_mm_movemask_epi8(_mm_cmpeq_epi8(haystack_sse, firstbyte)) == 0)
		{
			// look for first byte first; if not found, skip cmpestri, it's slow
			haystack += 16;
			haystacklen -= 16;
			continue;
		}
		
		int pos = _mm_cmpestri(needle_sse, needlelen, haystack_sse, 16, CMPSTR_FLAGS);
		
		if (pos < (int)step)
			return haystack + pos;
		
		haystack += step; // using 'pos' instead reduces iteration count, but using 'step' is faster. cmpestri latency is high
		haystacklen -= step;
	}
	
	// load_sse2_small_highundef fails for len=0, but haystacklen is known >= 1
	// the only way for it to be 0 is if step=16, which means needlelen=1, but that returns after memchr
	__m128i haystack_sse = load_sse2_small_highundef(haystack, haystacklen);
	int pos = _mm_cmpestri(needle_sse, needlelen, haystack_sse, haystacklen, CMPSTR_FLAGS);
	if (pos < (int)(haystacklen+1-needlelen))
		return haystack + pos;
#undef CMPSTR_FLAGS
	
	return NULL;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x170, %rsp            # imm = 0x170
movq %rdi, -0xe0(%rbp)
movq %rsi, -0xe8(%rbp)
movq %rdx, -0xf0(%rbp)
movq %rcx, -0xf8(%rbp)
movq -0xf0(%rbp), %rcx
movq -0xf8(%rbp), %rax
movq %rcx, -0x98(%rbp)
movq %rax, -0xa0(%rbp)
xorps %xmm0, %xmm0
movaps %xmm0, -0xb0(%rbp)
movq -0x98(%rbp), %rsi
movq -0xa0(%rbp), %rdx
leaq -0xb0(%rbp), %rdi
movq %rdi, -0x170(%rbp)
callq 0x4280
movq -0x170(%rbp), %rax
movq %rax, -0x10(%rbp)
movq -0x10(%rbp), %rax
movaps (%rax), %xmm0
movaps %xmm0, -0x110(%rbp)
movaps -0x110(%rbp), %xmm0
movaps %xmm0, -0x120(%rbp)
movaps -0x120(%rbp), %xmm0
movaps %xmm0, -0x80(%rbp)
movaps %xmm0, -0x90(%rbp)
movaps -0x80(%rbp), %xmm0
movaps -0x90(%rbp), %xmm1
punpcklbw %xmm1, %xmm0    # xmm0 = xmm0[0],xmm1[0],xmm0[1],xmm1[1],xmm0[2],xmm1[2],xmm0[3],xmm1[3],xmm0[4],xmm1[4],xmm0[5],xmm1[5],xmm0[6],xmm1[6],xmm0[7],xmm1[7]
movaps %xmm0, -0x120(%rbp)
movaps -0x120(%rbp), %xmm0
movaps %xmm0, -0x60(%rbp)
movaps %xmm0, -0x70(%rbp)
movaps -0x60(%rbp), %xmm0
movaps -0x70(%rbp), %xmm1
punpcklwd %xmm1, %xmm0    # xmm0 = xmm0[0],xmm1[0],xmm0[1],xmm1[1],xmm0[2],xmm1[2],xmm0[3],xmm1[3]
movaps %xmm0, -0x120(%rbp)
movaps -0x120(%rbp), %xmm0
pshufd $0x0, %xmm0, %xmm0      # xmm0 = xmm0[0,0,0,0]
movdqa %xmm0, -0x120(%rbp)
movl $0x11, %eax
subq -0xf8(%rbp), %rax
movq %rax, -0x128(%rbp)
cmpq $0x10, -0xe8(%rbp)
jb 0x2d4f3
movq -0xe0(%rbp), %rax
movq %rax, -0x48(%rbp)
movq -0x48(%rbp), %rax
movups (%rax), %xmm0
movaps %xmm0, -0x140(%rbp)
movaps -0x140(%rbp), %xmm1
movaps -0x120(%rbp), %xmm0
movaps %xmm1, -0x20(%rbp)
movaps %xmm0, -0x30(%rbp)
movaps -0x20(%rbp), %xmm0
movaps -0x30(%rbp), %xmm1
pcmpeqb %xmm1, %xmm0
movdqa %xmm0, -0x40(%rbp)
movdqa -0x40(%rbp), %xmm0
pmovmskb %xmm0, %eax
cmpl $0x0, %eax
jne 0x2d46b
movq -0xe0(%rbp), %rax
addq $0x10, %rax
movq %rax, -0xe0(%rbp)
movq -0xe8(%rbp), %rax
subq $0x10, %rax
movq %rax, -0xe8(%rbp)
jmp 0x2d3e6
movdqa -0x110(%rbp), %xmm0
movq -0xf8(%rbp), %rax
movdqa -0x140(%rbp), %xmm1
movl $0x10, %edx
pcmpestri $0xc, %xmm1, %xmm0
movl %ecx, -0x144(%rbp)
movl -0x144(%rbp), %eax
movq -0x128(%rbp), %rcx
cmpl %ecx, %eax
jge 0x2d4c1
movq -0xe0(%rbp), %rax
movslq -0x144(%rbp), %rcx
addq %rcx, %rax
movq %rax, -0xd8(%rbp)
jmp 0x2d5c4
movq -0x128(%rbp), %rax
addq -0xe0(%rbp), %rax
movq %rax, -0xe0(%rbp)
movq -0x128(%rbp), %rcx
movq -0xe8(%rbp), %rax
subq %rcx, %rax
movq %rax, -0xe8(%rbp)
jmp 0x2d3e6
movq -0xe0(%rbp), %rcx
movq -0xe8(%rbp), %rax
movq %rcx, -0xb8(%rbp)
movq %rax, -0xc0(%rbp)
leaq -0xd0(%rbp), %rdi
xorl %esi, %esi
movl $0x10, %edx
callq 0x4170
movq -0xb8(%rbp), %rsi
movq -0xc0(%rbp), %rdx
leaq -0xd0(%rbp), %rdi
callq 0x4280
leaq -0xd0(%rbp), %rax
movq %rax, -0x8(%rbp)
movq -0x8(%rbp), %rax
movdqa (%rax), %xmm0
movdqa %xmm0, -0x160(%rbp)
movdqa -0x110(%rbp), %xmm0
movq -0xf8(%rbp), %rax
movdqa -0x160(%rbp), %xmm1
movq -0xe8(%rbp), %rcx
movl %ecx, %edx
pcmpestri $0xc, %xmm1, %xmm0
movl %ecx, -0x164(%rbp)
movl -0x164(%rbp), %eax
movq -0xe8(%rbp), %rcx
addq $0x1, %rcx
subq -0xf8(%rbp), %rcx
cmpl %ecx, %eax
jge 0x2d5b9
movq -0xe0(%rbp), %rax
movslq -0x164(%rbp), %rcx
addq %rcx, %rax
movq %rax, -0xd8(%rbp)
jmp 0x2d5c4
movq $0x0, -0xd8(%rbp)
movq -0xd8(%rbp), %rax
addq $0x170, %rsp            # imm = 0x170
popq %rbp
retq
nopw %cs:(%rax,%rax)
nop