#include <machine/asm.h>
#include <machine/param.h>
#include "amd64_archlevel.h"
#define ALIGN_TEXT .p2align 4, 0x90
ARCHFUNCS(strncmp)
ARCHFUNC(strncmp, scalar)
ARCHFUNC(strncmp, baseline)
ENDARCHFUNCS(strncmp)
ARCHENTRY(strncmp, scalar)
xor %eax, %eax
sub $4, %rdx # 4 chars left to compare?
jbe 1f
ALIGN_TEXT
0: movzbl (%rdi), %ecx
test %ecx, %ecx # NUL char in first string?
jz .L0
cmpb (%rsi), %cl # mismatch between strings?
jnz .L0
movzbl 1(%rdi), %ecx
test %ecx, %ecx
jz .L1
cmpb 1(%rsi), %cl
jnz .L1
movzbl 2(%rdi), %ecx
test %ecx, %ecx
jz .L2
cmpb 2(%rsi), %cl
jnz .L2
movzbl 3(%rdi), %ecx
test %ecx, %ecx
jz .L3
cmpb 3(%rsi), %cl
jnz .L3
add $4, %rdi # advance to next iteration
add $4, %rsi
sub $4, %rdx
ja 0b
1: cmp $-4, %edx # end of string reached immediately?
jz .Leq
movzbl (%rdi), %ecx
test %ecx, %ecx
jz .L0
cmpb (%rsi), %cl
jnz .L0
cmp $-3, %edx # end of string reached after 1 char?
jz .Leq
movzbl 1(%rdi), %ecx
test %ecx, %ecx
jz .L1
cmpb 1(%rsi), %cl
jnz .L1
cmp $-2, %edx
jz .Leq
movzbl 2(%rdi), %ecx
test %ecx, %ecx
jz .L2
cmpb 2(%rsi), %cl
jnz .L2
cmp $-1, %edx # either end of string after 3 chars,
jz .Leq # or it boils down to the last char
.L3: inc %eax
.L2: inc %eax
.L1: inc %eax
.L0: movzbl (%rsi, %rax, 1), %ecx
movzbl (%rdi, %rax, 1), %eax
sub %ecx, %eax
.Leq: ret
ARCHEND(strncmp, scalar)
ARCHENTRY(strncmp, baseline)
push %rbx
sub $1, %rdx # RDX--, so RDX points to the last byte to compare
jb .Lempty # where there any bytes to compare at all?
lea 15(%rdi), %r8d # end of head
lea 15(%rsi), %r9d
mov %edi, %eax
mov %esi, %ebx
xor %edi, %r8d # bits that changed between first and last byte
xor %esi, %r9d
and $~0xf, %rdi # align heads to 16 bytes
and $~0xf, %rsi
or %r8d, %r9d
and $0xf, %eax # offset from alignment
and $0xf, %ebx
movdqa (%rdi), %xmm0 # load aligned heads
movdqa (%rsi), %xmm2
pxor %xmm1, %xmm1
cmp $16, %rdx # end of buffer within the first 32 bytes?
jb .Llt16
test $PAGE_SIZE, %r9d # did the page change?
jz 0f # if not, take fast path
movdqa %xmm0, -32(%rsp) # stash copies of the heads on the stack
movdqa %xmm2, -16(%rsp)
mov $-1, %r8d
mov $-1, %r9d
mov %eax, %ecx
shl %cl, %r8d # string head in XMM0
mov %ebx, %ecx
shl %cl, %r9d # string head in XMM2
pcmpeqb %xmm1, %xmm0
pcmpeqb %xmm1, %xmm2
pmovmskb %xmm0, %r10d
pmovmskb %xmm2, %r11d
test %r8d, %r10d # NUL byte present in first string?
lea -32(%rsp), %r8
cmovz %rdi, %r8
test %r9d, %r11d # NUL byte present in second string?
lea -16(%rsp), %r9
cmovz %rsi, %r9
movdqu (%r8, %rax, 1), %xmm0 # load true (or fake) heads
movdqu (%r9, %rbx, 1), %xmm4
jmp 1f
.Lempty:
xor %eax, %eax # zero-length buffers compare equal
pop %rbx
ret
0: movdqu (%rdi, %rax, 1), %xmm0 # load true heads
movdqu (%rsi, %rbx, 1), %xmm4
1: pxor %xmm2, %xmm2
pcmpeqb %xmm0, %xmm2 # NUL byte present?
pcmpeqb %xmm0, %xmm4 # which bytes match?
pandn %xmm4, %xmm2 # match and not NUL byte?
pmovmskb %xmm2, %r9d
xor $0xffff, %r9d # mismatch or NUL byte?
jnz .Lhead_mismatch
movdqa 16(%rdi), %xmm2 # load second chunks
movdqa 16(%rsi), %xmm3
lea -16(%rdx, %rbx, 1), %rdx # account for length of RSI chunk
sub %rbx, %rax # is a&0xf >= b&0xf?
jb .Lswapped # if not, proceed with swapped operands
jmp .Lnormal
.Llt16: test $PAGE_SIZE, %r9d # did the page change?
jz 0f # if not, take fast path
movdqa %xmm0, -32(%rsp) # stash copies of the heads on the stack
movdqa %xmm2, -16(%rsp)
mov $-1, %r8d
mov $-1, %r9d
mov %eax, %ecx
shl %cl, %r8d # string head in XMM0
mov %ebx, %ecx
shl %cl, %r9d # string head in XMM2
pcmpeqb %xmm1, %xmm0
pcmpeqb %xmm1, %xmm2
pmovmskb %xmm0, %r10d
pmovmskb %xmm2, %r11d
lea (%rdx, %rax, 1), %ecx # location of last buffer byte in xmm0
bts %ecx, %r10d # treat as if NUL byte present
lea (%rdx, %rbx, 1), %ecx
bts %ecx, %r11d
test %r8w, %r10w # NUL byte present in first string head?
lea -32(%rsp), %r8
cmovz %rdi, %r8
test %r9w, %r11w # NUL byte present in second string head?
lea -16(%rsp), %r9
cmovz %rsi, %r9
movdqu (%r8, %rax, 1), %xmm0 # load true (or fake) heads
movdqu (%r9, %rbx, 1), %xmm4
jmp 1f
0: movdqu (%rdi, %rax, 1), %xmm0 # load true heads
movdqu (%rsi, %rbx, 1), %xmm4
1: pxor %xmm2, %xmm2
pcmpeqb %xmm0, %xmm2 # NUL byte present?
pcmpeqb %xmm0, %xmm4 # which bytes match?
pandn %xmm4, %xmm2 # match and not NUL byte?
pmovmskb %xmm2, %r9d
btr %edx, %r9d # induce mismatch in last byte of buffer
not %r9d # mismatch or NUL byte?
ALIGN_TEXT
.Lhead_mismatch:
tzcnt %r9d, %r9d # where is the mismatch?
add %rax, %rdi # return to true heads
add %rbx, %rsi
movzbl (%rdi, %r9, 1), %eax # mismatching characters
movzbl (%rsi, %r9, 1), %ecx
sub %ecx, %eax
pop %rbx
ret
ALIGN_TEXT
.Lnormal:
neg %rax
movdqu 16(%rsi, %rax, 1), %xmm0
sub %rdi, %rsi # express RSI as distance from RDI
lea (%rsi, %rax, 1), %rbx # point RBX to offset in second string
neg %rax # ... corresponding to RDI
pcmpeqb %xmm3, %xmm1 # NUL present?
pcmpeqb %xmm2, %xmm0 # Mismatch between chunks?
pmovmskb %xmm1, %r8d
pmovmskb %xmm0, %r9d
mov $16, %ecx
cmp %rcx, %rdx # does the buffer end within (RDI,RSI,1)?
cmovb %edx, %ecx # ECX = min(16, RDX)
add $32, %rdi # advance to next iteration
bts %ecx, %r8d # mark end-of-buffer as if there was a NUL byte
test %r8w, %r8w # NUL or end of buffer found?
jnz .Lnul_found2
xor $0xffff, %r9d
jnz .Lmismatch2
sub $48, %rdx # end of buffer within first main loop iteration?
jb .Ltail # if yes, process tail
ALIGN_TEXT
0: movdqu (%rdi, %rbx, 1), %xmm0 # chunk of 2nd string corresponding to RDI
pxor %xmm1, %xmm1
pcmpeqb (%rdi, %rsi, 1), %xmm1 # end of string in RSI?
pcmpeqb (%rdi), %xmm0 # where do the chunks match?
pmovmskb %xmm1, %r8d
pmovmskb %xmm0, %r9d
test %r8d, %r8d
jnz .Lnul_found
xor $0xffff, %r9d # any mismatches?
jnz .Lmismatch
movdqu 16(%rdi, %rbx, 1), %xmm0
pxor %xmm1, %xmm1
pcmpeqb 16(%rdi, %rsi, 1), %xmm1
pcmpeqb 16(%rdi), %xmm0
pmovmskb %xmm1, %r8d
pmovmskb %xmm0, %r9d
add $32, %rdi
test %r8d, %r8d
jnz .Lnul_found2
xor $0xffff, %r9d
jnz .Lmismatch2
sub $32, %rdx # end of buffer within next iteration?
jae 0b
.Ltail: movdqu (%rdi, %rbx, 1), %xmm0 # chunk of 2nd string corresponding to RDI
pxor %xmm1, %xmm1
pcmpeqb (%rdi, %rsi, 1), %xmm1 # end of string in RSI?
pcmpeqb (%rdi), %xmm0 # where do the chunks match?
pmovmskb %xmm1, %r8d
pmovmskb %xmm0, %r9d
bts %edx, %r8d # indicate NUL byte at last byte in buffer
test %r8w, %r8w # NUL byte in first chunk?
jnz .Lnul_found
xor $0xffff, %r9d # any mismatches?
jnz .Lmismatch
movdqu 16(%rdi, %rbx, 1), %xmm0
pxor %xmm1, %xmm1
pcmpeqb 16(%rdi, %rsi, 1), %xmm1
pcmpeqb 16(%rdi), %xmm0
pmovmskb %xmm1, %r8d
pmovmskb %xmm0, %r9d
sub $16, %edx # take first half into account
bts %edx, %r8d # indicate NUL byte at last byte in buffer
add $32, %rdi
.Lnul_found2:
sub $16, %rdi
.Lnul_found:
mov %eax, %ecx
mov %r8d, %r10d
shl %cl, %r8d # adjust NUL mask to positions in RDI/RBX
not %r9d # mask of mismatches
or %r8w, %r9w # NUL bytes als count as mismatches
jnz .Lmismatch
movdqu (%rdi, %rax, 1), %xmm0
pcmpeqb (%rdi, %rsi, 1), %xmm0
add %rdi, %rsi # restore RSI pointer
add %rax, %rdi # point RDI to chunk corresponding to (RSI)
pmovmskb %xmm0, %ecx # mask of matches
not %ecx # mask of mismatches
or %r10d, %ecx # mask of mismatches or NUL bytes
tzcnt %ecx, %ecx # location of first mismatch
movzbl (%rdi, %rcx, 1), %eax
movzbl (%rsi, %rcx, 1), %ecx
sub %ecx, %eax
pop %rbx
ret
.Lmismatch2:
sub $16, %rdi
.Lmismatch:
tzcnt %r9d, %r9d # where is the mismatch?
add %rdi, %rbx # turn RBX from offset into pointer
movzbl (%rbx, %r9, 1), %ecx
movzbl (%rdi, %r9, 1), %eax
sub %ecx, %eax
pop %rbx
ret
ALIGN_TEXT
.Lswapped:
movdqu 16(%rdi, %rax, 1), %xmm0
sub %rsi, %rdi # express RDI as distance from RDI
lea (%rdi, %rax, 1), %rbx # point RBX to offset in first string
pcmpeqb %xmm2, %xmm1 # NUL present?
pcmpeqb %xmm3, %xmm0 # mismatch between chunks?
pmovmskb %xmm1, %r8d
pmovmskb %xmm0, %r9d
add %rax, %rdx # RDX points to buffer end in RSI
neg %rax # ... corresponding to RSI
mov $16, %ecx
cmp %rcx, %rdx # does the buffer end within (RSI,RDI,1)?
cmovb %edx, %ecx # ECX = min(16, RDX)
add $32, %rsi
bts %ecx, %r8d # mark end-of-buffer as if there was a NUL byte
test %r8w, %r8w # NUL or end of buffer found?
jnz .Lnul_found2s
xor $0xffff, %r9d
jnz .Lmismatch2s
sub $48, %rdx # end of buffer within first main loop iteration?
jb .Ltails # if yes, process tail
ALIGN_TEXT
0: movdqu (%rsi, %rbx, 1), %xmm0 # chunk of 1st string corresponding to RSI
pxor %xmm1, %xmm1
pcmpeqb (%rsi, %rdi, 1), %xmm1 # end of string in RDI?
pcmpeqb (%rsi), %xmm0 # where do the chunks match?
pmovmskb %xmm1, %r8d
pmovmskb %xmm0, %r9d
test %r8d, %r8d
jnz .Lnul_founds
xor $0xffff, %r9d # any mismatches?
jnz .Lmismatchs
movdqu 16(%rsi, %rbx, 1), %xmm0
pxor %xmm1, %xmm1
pcmpeqb 16(%rsi, %rdi, 1), %xmm1
pcmpeqb 16(%rsi), %xmm0
pmovmskb %xmm1, %r8d
pmovmskb %xmm0, %r9d
add $32, %rsi
test %r8d, %r8d
jnz .Lnul_found2s
xor $0xffff, %r9d
jnz .Lmismatch2s
sub $32, %rdx # end of buffer within next iteration?
jae 0b
.Ltails:
movdqu (%rsi, %rbx, 1), %xmm0 # chunk of 1st string corresponding to RSI
pxor %xmm1, %xmm1
pcmpeqb (%rsi, %rdi, 1), %xmm1 # end of string in RDI?
pcmpeqb (%rsi), %xmm0 # where do the chunks match?
pmovmskb %xmm1, %r8d
pmovmskb %xmm0, %r9d
bts %edx, %r8d # indicate NUL byte at laste byte in buffer
test %r8w, %r8w # NUL byte in first chunk?
jnz .Lnul_founds
xor $0xffff, %r9d # any mismatches?
jnz .Lmismatchs
movdqu 16(%rsi, %rbx, 1), %xmm0
pxor %xmm1, %xmm1
pcmpeqb 16(%rsi, %rdi, 1), %xmm1
pcmpeqb 16(%rsi), %xmm0
pmovmskb %xmm1, %r8d
pmovmskb %xmm0, %r9d
sub $16, %edx # take first half into account
bts %edx, %r8d # indicate NUL byte at laste byte in buffer
add $32, %rsi
.Lnul_found2s:
sub $16, %rsi
.Lnul_founds:
mov %eax, %ecx
mov %r8d, %r10d
shl %cl, %r8d # adjust NUL mask to positions in RSI/RBX
not %r9d # mask of mismatches
or %r8w, %r9w # NUL bytes also count as mismatches
jnz .Lmismatchs
movdqu (%rsi, %rax, 1), %xmm0
pcmpeqb (%rsi, %rdi, 1), %xmm0
add %rsi, %rdi # restore RDI pointer
add %rax, %rsi # point RSI to chunk corresponding to (RDI)
pmovmskb %xmm0, %ecx # mask of matches
not %ecx # mask of mismatches
or %r10d, %ecx # mask of mismatches or NUL bytes
tzcnt %ecx, %ecx # location of first mismatch
movzbl (%rdi, %rcx, 1), %eax
movzbl (%rsi, %rcx, 1), %ecx
sub %ecx, %eax
pop %rbx
ret
.Lmismatch2s:
sub $16, %rsi
.Lmismatchs:
tzcnt %r9d, %r9d # where is the mismatch?
add %rsi, %rbx # turn RBX from offset into pointer
movzbl (%rbx, %r9, 1), %eax
movzbl (%rsi, %r9, 1), %ecx
sub %ecx, %eax
pop %rbx
ret
ARCHEND(strncmp, baseline)
.section .note.GNU-stack,"",%progbits