/* memcpy optimized with SSE2 unaligned memory access instructions.
Copyright (C) 2014-2023 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
. */
#if IS_IN (libc) \
&& (defined SHARED \
|| defined USE_AS_MEMMOVE \
|| !defined USE_MULTIARCH)
# include
# include "asm-syntax.h"
# ifndef MEMCPY
# define MEMCPY __memcpy_sse2_unaligned
# define MEMCPY_CHK __memcpy_chk_sse2_unaligned
# endif
# define DEST PARMS
# define SRC DEST+4
# define LEN SRC+4
# define CFI_PUSH(REG) \
cfi_adjust_cfa_offset (4); \
cfi_rel_offset (REG, 0)
# define CFI_POP(REG) \
cfi_adjust_cfa_offset (-4); \
cfi_restore (REG)
# define PUSH(REG) pushl REG; CFI_PUSH (REG)
# define POP(REG) popl REG; CFI_POP (REG)
# define PARMS 8 /* Preserve EBX. */
# define ENTRANCE PUSH (%ebx);
# define RETURN_END POP (%ebx); ret
# define RETURN RETURN_END; CFI_PUSH (%ebx)
.section .text.sse2,"ax",@progbits
# if defined SHARED
ENTRY (MEMCPY_CHK)
movl 12(%esp), %eax
cmpl %eax, 16(%esp)
jb HIDDEN_JUMPTARGET (__chk_fail)
END (MEMCPY_CHK)
# endif
ENTRY (MEMCPY)
ENTRANCE
movl LEN(%esp), %ecx
movl SRC(%esp), %eax
movl DEST(%esp), %edx
cmp %edx, %eax
# ifdef USE_AS_MEMMOVE
ja L(check_forward)
L(mm_len_0_or_more_backward):
/* Now do checks for lengths. We do [0..16], [16..32], [32..64], [64..128]
separately. */
cmp $16, %ecx
jbe L(mm_len_0_16_bytes_backward)
cmpl $32, %ecx
ja L(mm_len_32_or_more_backward)
/* Copy [0..32] and return. */
movdqu (%eax), %xmm0
movdqu -16(%eax, %ecx), %xmm1
movdqu %xmm0, (%edx)
movdqu %xmm1, -16(%edx, %ecx)
jmp L(return)
L(mm_len_32_or_more_backward):
cmpl $64, %ecx
ja L(mm_len_64_or_more_backward)
/* Copy [0..64] and return. */
movdqu (%eax), %xmm0
movdqu 16(%eax), %xmm1
movdqu -16(%eax, %ecx), %xmm2
movdqu -32(%eax, %ecx), %xmm3
movdqu %xmm0, (%edx)
movdqu %xmm1, 16(%edx)
movdqu %xmm2, -16(%edx, %ecx)
movdqu %xmm3, -32(%edx, %ecx)
jmp L(return)
L(mm_len_64_or_more_backward):
cmpl $128, %ecx
ja L(mm_len_128_or_more_backward)
/* Copy [0..128] and return. */
movdqu (%eax), %xmm0
movdqu 16(%eax), %xmm1
movdqu 32(%eax), %xmm2
movdqu 48(%eax), %xmm3
movdqu -64(%eax, %ecx), %xmm4
movdqu -48(%eax, %ecx), %xmm5
movdqu -32(%eax, %ecx), %xmm6
movdqu -16(%eax, %ecx), %xmm7
movdqu %xmm0, (%edx)
movdqu %xmm1, 16(%edx)
movdqu %xmm2, 32(%edx)
movdqu %xmm3, 48(%edx)
movdqu %xmm4, -64(%edx, %ecx)
movdqu %xmm5, -48(%edx, %ecx)
movdqu %xmm6, -32(%edx, %ecx)
movdqu %xmm7, -16(%edx, %ecx)
jmp L(return)
L(mm_len_128_or_more_backward):
add %ecx, %eax
cmp %edx, %eax
movl SRC(%esp), %eax
jbe L(forward)
PUSH (%esi)
PUSH (%edi)
PUSH (%ebx)
/* Aligning the address of destination. */
movdqu (%eax), %xmm4
movdqu 16(%eax), %xmm5
movdqu 32(%eax), %xmm6
movdqu 48(%eax), %xmm7
leal (%edx, %ecx), %esi
movdqu -16(%eax, %ecx), %xmm0
subl $16, %esp
movdqu %xmm0, (%esp)
mov %ecx, %edi
movl %esi, %ecx
andl $-16, %ecx
leal (%ecx), %ebx
subl %edx, %ebx
leal (%eax, %ebx), %eax
shrl $6, %ebx
# ifdef SHARED_CACHE_SIZE_HALF
cmp $SHARED_CACHE_SIZE_HALF, %edi
# else
# ifdef PIC
PUSH (%ebx)
SETUP_PIC_REG (bx)
add $_GLOBAL_OFFSET_TABLE_, %ebx
cmp __x86_shared_cache_size_half@GOTOFF(%ebx), %edi
POP (%ebx)
# else
cmp __x86_shared_cache_size_half, %edi
# endif
# endif
jae L(mm_large_page_loop_backward)
.p2align 4
L(mm_main_loop_backward):
prefetcht0 -128(%eax)
movdqu -64(%eax), %xmm0
movdqu -48(%eax), %xmm1
movdqu -32(%eax), %xmm2
movdqu -16(%eax), %xmm3
movaps %xmm0, -64(%ecx)
subl $64, %eax
movaps %xmm1, -48(%ecx)
movaps %xmm2, -32(%ecx)
movaps %xmm3, -16(%ecx)
subl $64, %ecx
sub $1, %ebx
jnz L(mm_main_loop_backward)
movdqu (%esp), %xmm0
addl $16, %esp
movdqu %xmm0, -16(%esi)
movdqu %xmm4, (%edx)
movdqu %xmm5, 16(%edx)
movdqu %xmm6, 32(%edx)
movdqu %xmm7, 48(%edx)
POP (%ebx)
jmp L(mm_return_pop_all)
/* Copy [0..16] and return. */
L(mm_len_0_16_bytes_backward):
testb $24, %cl
jnz L(mm_len_9_16_bytes_backward)
testb $4, %cl
.p2align 4,,5
jnz L(mm_len_5_8_bytes_backward)
testl %ecx, %ecx
.p2align 4,,2
je L(return)
testb $2, %cl
.p2align 4,,1
jne L(mm_len_3_4_bytes_backward)
movzbl -1(%eax,%ecx), %ebx
movzbl (%eax), %eax
movb %bl, -1(%edx,%ecx)
movb %al, (%edx)
jmp L(return)
L(mm_len_3_4_bytes_backward):
movzwl -2(%eax,%ecx), %ebx
movzwl (%eax), %eax
movw %bx, -2(%edx,%ecx)
movw %ax, (%edx)
jmp L(return)
L(mm_len_9_16_bytes_backward):
PUSH (%esi)
movl -4(%eax,%ecx), %ebx
movl -8(%eax,%ecx), %esi
movl %ebx, -4(%edx,%ecx)
movl %esi, -8(%edx,%ecx)
subl $8, %ecx
POP (%esi)
jmp L(mm_len_0_16_bytes_backward)
L(mm_len_5_8_bytes_backward):
movl (%eax), %ebx
movl -4(%eax,%ecx), %eax
movl %ebx, (%edx)
movl %eax, -4(%edx,%ecx)
jmp L(return)
/* Big length copy backward part. */
.p2align 4
L(mm_large_page_loop_backward):
movdqu -64(%eax), %xmm0
movdqu -48(%eax), %xmm1
movdqu -32(%eax), %xmm2
movdqu -16(%eax), %xmm3
movntdq %xmm0, -64(%ecx)
subl $64, %eax
movntdq %xmm1, -48(%ecx)
movntdq %xmm2, -32(%ecx)
movntdq %xmm3, -16(%ecx)
subl $64, %ecx
sub $1, %ebx
jnz L(mm_large_page_loop_backward)
sfence
movdqu (%esp), %xmm0
addl $16, %esp
movdqu %xmm0, -16(%esi)
movdqu %xmm4, (%edx)
movdqu %xmm5, 16(%edx)
movdqu %xmm6, 32(%edx)
movdqu %xmm7, 48(%edx)
POP (%ebx)
jmp L(mm_return_pop_all)
L(check_forward):
add %edx, %ecx
cmp %eax, %ecx
movl LEN(%esp), %ecx
jbe L(forward)
/* Now do checks for lengths. We do [0..16], [0..32], [0..64], [0..128]
separately. */
cmp $16, %ecx
jbe L(mm_len_0_16_bytes_forward)
cmpl $32, %ecx
ja L(mm_len_32_or_more_forward)
/* Copy [0..32] and return. */
movdqu (%eax), %xmm0
movdqu -16(%eax, %ecx), %xmm1
movdqu %xmm0, (%edx)
movdqu %xmm1, -16(%edx, %ecx)
jmp L(return)
L(mm_len_32_or_more_forward):
cmpl $64, %ecx
ja L(mm_len_64_or_more_forward)
/* Copy [0..64] and return. */
movdqu (%eax), %xmm0
movdqu 16(%eax), %xmm1
movdqu -16(%eax, %ecx), %xmm2
movdqu -32(%eax, %ecx), %xmm3
movdqu %xmm0, (%edx)
movdqu %xmm1, 16(%edx)
movdqu %xmm2, -16(%edx, %ecx)
movdqu %xmm3, -32(%edx, %ecx)
jmp L(return)
L(mm_len_64_or_more_forward):
cmpl $128, %ecx
ja L(mm_len_128_or_more_forward)
/* Copy [0..128] and return. */
movdqu (%eax), %xmm0
movdqu 16(%eax), %xmm1
movdqu 32(%eax), %xmm2
movdqu 48(%eax), %xmm3
movdqu -64(%eax, %ecx), %xmm4
movdqu -48(%eax, %ecx), %xmm5
movdqu -32(%eax, %ecx), %xmm6
movdqu -16(%eax, %ecx), %xmm7
movdqu %xmm0, (%edx)
movdqu %xmm1, 16(%edx)
movdqu %xmm2, 32(%edx)
movdqu %xmm3, 48(%edx)
movdqu %xmm4, -64(%edx, %ecx)
movdqu %xmm5, -48(%edx, %ecx)
movdqu %xmm6, -32(%edx, %ecx)
movdqu %xmm7, -16(%edx, %ecx)
jmp L(return)
L(mm_len_128_or_more_forward):
PUSH (%esi)
PUSH (%edi)
PUSH (%ebx)
/* Aligning the address of destination. */
movdqu -16(%eax, %ecx), %xmm4
movdqu -32(%eax, %ecx), %xmm5
movdqu -48(%eax, %ecx), %xmm6
movdqu -64(%eax, %ecx), %xmm7
leal (%edx, %ecx), %esi
movdqu (%eax), %xmm0
subl $16, %esp
movdqu %xmm0, (%esp)
mov %ecx, %edi
leal 16(%edx), %ecx
andl $-16, %ecx
movl %ecx, %ebx
subl %edx, %ebx
addl %ebx, %eax
movl %esi, %ebx
subl %ecx, %ebx
shrl $6, %ebx
# ifdef SHARED_CACHE_SIZE_HALF
cmp $SHARED_CACHE_SIZE_HALF, %edi
# else
# ifdef PIC
PUSH (%ebx)
SETUP_PIC_REG(bx)
add $_GLOBAL_OFFSET_TABLE_, %ebx
cmp __x86_shared_cache_size_half@GOTOFF(%ebx), %edi
POP (%ebx)
# else
cmp __x86_shared_cache_size_half, %edi
# endif
# endif
jae L(mm_large_page_loop_forward)
.p2align 4
L(mm_main_loop_forward):
prefetcht0 128(%eax)
movdqu (%eax), %xmm0
movdqu 16(%eax), %xmm1
movdqu 32(%eax), %xmm2
movdqu 48(%eax), %xmm3
movdqa %xmm0, (%ecx)
addl $64, %eax
movaps %xmm1, 16(%ecx)
movaps %xmm2, 32(%ecx)
movaps %xmm3, 48(%ecx)
addl $64, %ecx
sub $1, %ebx
jnz L(mm_main_loop_forward)
movdqu (%esp), %xmm0
addl $16, %esp
movdqu %xmm0, (%edx)
movdqu %xmm4, -16(%esi)
movdqu %xmm5, -32(%esi)
movdqu %xmm6, -48(%esi)
movdqu %xmm7, -64(%esi)
POP (%ebx)
jmp L(mm_return_pop_all)
L(mm_len_0_16_bytes_forward):
testb $24, %cl
jne L(mm_len_9_16_bytes_forward)
testb $4, %cl
.p2align 4,,5
jne L(mm_len_5_8_bytes_forward)
testl %ecx, %ecx
.p2align 4,,2
je L(return)
testb $2, %cl
.p2align 4,,1
jne L(mm_len_2_4_bytes_forward)
movzbl -1(%eax,%ecx), %ebx
movzbl (%eax), %eax
movb %bl, -1(%edx,%ecx)
movb %al, (%edx)
jmp L(return)
L(mm_len_2_4_bytes_forward):
movzwl -2(%eax,%ecx), %ebx
movzwl (%eax), %eax
movw %bx, -2(%edx,%ecx)
movw %ax, (%edx)
jmp L(return)
L(mm_len_5_8_bytes_forward):
movl (%eax), %ebx
movl -4(%eax,%ecx), %eax
movl %ebx, (%edx)
movl %eax, -4(%edx,%ecx)
jmp L(return)
L(mm_len_9_16_bytes_forward):
movq (%eax), %xmm0
movq -8(%eax, %ecx), %xmm1
movq %xmm0, (%edx)
movq %xmm1, -8(%edx, %ecx)
jmp L(return)
L(mm_return_pop_all):
movl %edx, %eax
POP (%edi)
POP (%esi)
RETURN
/* Big length copy forward part. */
.p2align 4
L(mm_large_page_loop_forward):
movdqu (%eax), %xmm0
movdqu 16(%eax), %xmm1
movdqu 32(%eax), %xmm2
movdqu 48(%eax), %xmm3
movntdq %xmm0, (%ecx)
addl $64, %eax
movntdq %xmm1, 16(%ecx)
movntdq %xmm2, 32(%ecx)
movntdq %xmm3, 48(%ecx)
addl $64, %ecx
sub $1, %ebx
jnz L(mm_large_page_loop_forward)
sfence
movdqu (%esp), %xmm0
addl $16, %esp
movdqu %xmm0, (%edx)
movdqu %xmm4, -16(%esi)
movdqu %xmm5, -32(%esi)
movdqu %xmm6, -48(%esi)
movdqu %xmm7, -64(%esi)
POP (%ebx)
jmp L(mm_return_pop_all)
# endif
L(forward):
cmp $16, %ecx
jbe L(len_0_16_bytes)
# ifdef SHARED_CACHE_SIZE_HALF
cmp $SHARED_CACHE_SIZE_HALF, %ecx
# else
# ifdef PIC
SETUP_PIC_REG(bx)
add $_GLOBAL_OFFSET_TABLE_, %ebx
cmp __x86_shared_cache_size_half@GOTOFF(%ebx), %ecx
# else
cmp __x86_shared_cache_size_half, %ecx
# endif
# endif
jae L(large_page)
movdqu (%eax), %xmm0
movdqu -16(%eax, %ecx), %xmm1
cmpl $32, %ecx
movdqu %xmm0, (%edx)
movdqu %xmm1, -16(%edx, %ecx)
jbe L(return)
movdqu 16(%eax), %xmm0
movdqu -32(%eax, %ecx), %xmm1
cmpl $64, %ecx
movdqu %xmm0, 16(%edx)
movdqu %xmm1, -32(%edx, %ecx)
jbe L(return)
movdqu 32(%eax), %xmm0
movdqu 48(%eax), %xmm1
movdqu -48(%eax, %ecx), %xmm2
movdqu -64(%eax, %ecx), %xmm3
cmpl $128, %ecx
movdqu %xmm0, 32(%edx)
movdqu %xmm1, 48(%edx)
movdqu %xmm2, -48(%edx, %ecx)
movdqu %xmm3, -64(%edx, %ecx)
jbe L(return)
/* Now the main loop: we align the address of the destination. */
leal 64(%edx), %ebx
andl $-64, %ebx
addl %edx, %ecx
andl $-64, %ecx
subl %edx, %eax
/* We should stop two iterations before the termination
(in order not to misprefetch). */
subl $64, %ecx
cmpl %ebx, %ecx
je L(main_loop_just_one_iteration)
subl $64, %ecx
cmpl %ebx, %ecx
je L(main_loop_last_two_iterations)
.p2align 4
L(main_loop_cache):
prefetcht0 128(%ebx, %eax)
movdqu (%ebx, %eax), %xmm0
movdqu 16(%ebx, %eax), %xmm1
movdqu 32(%ebx, %eax), %xmm2
movdqu 48(%ebx, %eax), %xmm3
movdqa %xmm0, (%ebx)
movaps %xmm1, 16(%ebx)
movaps %xmm2, 32(%ebx)
movaps %xmm3, 48(%ebx)
lea 64(%ebx), %ebx
cmpl %ebx, %ecx
jne L(main_loop_cache)
L(main_loop_last_two_iterations):
movdqu (%ebx, %eax), %xmm0
movdqu 16(%ebx, %eax), %xmm1
movdqu 32(%ebx, %eax), %xmm2
movdqu 48(%ebx, %eax), %xmm3
movdqu 64(%ebx, %eax), %xmm4
movdqu 80(%ebx, %eax), %xmm5
movdqu 96(%ebx, %eax), %xmm6
movdqu 112(%ebx, %eax), %xmm7
movdqa %xmm0, (%ebx)
movaps %xmm1, 16(%ebx)
movaps %xmm2, 32(%ebx)
movaps %xmm3, 48(%ebx)
movaps %xmm4, 64(%ebx)
movaps %xmm5, 80(%ebx)
movaps %xmm6, 96(%ebx)
movaps %xmm7, 112(%ebx)
jmp L(return)
L(main_loop_just_one_iteration):
movdqu (%ebx, %eax), %xmm0
movdqu 16(%ebx, %eax), %xmm1
movdqu 32(%ebx, %eax), %xmm2
movdqu 48(%ebx, %eax), %xmm3
movdqa %xmm0, (%ebx)
movaps %xmm1, 16(%ebx)
movaps %xmm2, 32(%ebx)
movaps %xmm3, 48(%ebx)
jmp L(return)
L(large_page):
movdqu (%eax), %xmm0
movdqu 16(%eax), %xmm1
movdqu 32(%eax), %xmm2
movdqu 48(%eax), %xmm3
movdqu -64(%eax, %ecx), %xmm4
movdqu -48(%eax, %ecx), %xmm5
movdqu -32(%eax, %ecx), %xmm6
movdqu -16(%eax, %ecx), %xmm7
movdqu %xmm0, (%edx)
movdqu %xmm1, 16(%edx)
movdqu %xmm2, 32(%edx)
movdqu %xmm3, 48(%edx)
movdqu %xmm4, -64(%edx, %ecx)
movdqu %xmm5, -48(%edx, %ecx)
movdqu %xmm6, -32(%edx, %ecx)
movdqu %xmm7, -16(%edx, %ecx)
movdqu 64(%eax), %xmm0
movdqu 80(%eax), %xmm1
movdqu 96(%eax), %xmm2
movdqu 112(%eax), %xmm3
movdqu -128(%eax, %ecx), %xmm4
movdqu -112(%eax, %ecx), %xmm5
movdqu -96(%eax, %ecx), %xmm6
movdqu -80(%eax, %ecx), %xmm7
movdqu %xmm0, 64(%edx)
movdqu %xmm1, 80(%edx)
movdqu %xmm2, 96(%edx)
movdqu %xmm3, 112(%edx)
movdqu %xmm4, -128(%edx, %ecx)
movdqu %xmm5, -112(%edx, %ecx)
movdqu %xmm6, -96(%edx, %ecx)
movdqu %xmm7, -80(%edx, %ecx)
/* Now the main loop with non temporal stores. We align
the address of the destination. */
leal 128(%edx), %ebx
andl $-128, %ebx
addl %edx, %ecx
andl $-128, %ecx
subl %edx, %eax
.p2align 4
L(main_loop_large_page):
movdqu (%ebx, %eax), %xmm0
movdqu 16(%ebx, %eax), %xmm1
movdqu 32(%ebx, %eax), %xmm2
movdqu 48(%ebx, %eax), %xmm3
movdqu 64(%ebx, %eax), %xmm4
movdqu 80(%ebx, %eax), %xmm5
movdqu 96(%ebx, %eax), %xmm6
movdqu 112(%ebx, %eax), %xmm7
movntdq %xmm0, (%ebx)
movntdq %xmm1, 16(%ebx)
movntdq %xmm2, 32(%ebx)
movntdq %xmm3, 48(%ebx)
movntdq %xmm4, 64(%ebx)
movntdq %xmm5, 80(%ebx)
movntdq %xmm6, 96(%ebx)
movntdq %xmm7, 112(%ebx)
lea 128(%ebx), %ebx
cmpl %ebx, %ecx
jne L(main_loop_large_page)
sfence
jmp L(return)
L(len_0_16_bytes):
testb $24, %cl
jne L(len_9_16_bytes)
testb $4, %cl
.p2align 4,,5
jne L(len_5_8_bytes)
testl %ecx, %ecx
.p2align 4,,2
je L(return)
movzbl (%eax), %ebx
testb $2, %cl
movb %bl, (%edx)
je L(return)
movzwl -2(%eax,%ecx), %ebx
movw %bx, -2(%edx,%ecx)
jmp L(return)
L(len_9_16_bytes):
movq (%eax), %xmm0
movq -8(%eax, %ecx), %xmm1
movq %xmm0, (%edx)
movq %xmm1, -8(%edx, %ecx)
jmp L(return)
L(len_5_8_bytes):
movl (%eax), %ebx
movl %ebx, (%edx)
movl -4(%eax,%ecx), %ebx
movl %ebx, -4(%edx,%ecx)
L(return):
movl %edx, %eax
# ifdef USE_AS_MEMPCPY
movl LEN(%esp), %ecx
add %ecx, %eax
# endif
RETURN
END (MEMCPY)
#endif