// SPDX-License-Identifier: GPL-2.0-only
/*
* Copy memory to memory until the specified number of bytes
* has been copied. Overlap is NOT handled correctly.
* Copyright (C) 1991-2020 Free Software Foundation, Inc.
* This file is part of the GNU C Library.
* Contributed by Torbjorn Granlund (tege@sics.se).
*
* 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
* .
*
*/
#define __NO_FORTIFY
#include
#include
#define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2)))
#define OP_T_THRES 16
#define op_t unsigned long
#define OPSIZ (sizeof(op_t))
#define OPSIZ_MASK (sizeof(op_t) - 1)
#define FAST_COPY_THRES (128)
#define byte unsigned char
static void _wordcopy_fwd_aligned(long dstp, long srcp, size_t len)
{
op_t a0, a1;
switch (len % 8) {
case 2:
a0 = ((op_t *) srcp)[0];
srcp -= 6 * OPSIZ;
dstp -= 7 * OPSIZ;
len += 6;
goto do1;
case 3:
a1 = ((op_t *) srcp)[0];
srcp -= 5 * OPSIZ;
dstp -= 6 * OPSIZ;
len += 5;
goto do2;
case 4:
a0 = ((op_t *) srcp)[0];
srcp -= 4 * OPSIZ;
dstp -= 5 * OPSIZ;
len += 4;
goto do3;
case 5:
a1 = ((op_t *) srcp)[0];
srcp -= 3 * OPSIZ;
dstp -= 4 * OPSIZ;
len += 3;
goto do4;
case 6:
a0 = ((op_t *) srcp)[0];
srcp -= 2 * OPSIZ;
dstp -= 3 * OPSIZ;
len += 2;
goto do5;
case 7:
a1 = ((op_t *) srcp)[0];
srcp -= 1 * OPSIZ;
dstp -= 2 * OPSIZ;
len += 1;
goto do6;
case 0:
if (OP_T_THRES <= 3 * OPSIZ && len == 0)
return;
a0 = ((op_t *) srcp)[0];
srcp -= 0 * OPSIZ;
dstp -= 1 * OPSIZ;
goto do7;
case 1:
a1 = ((op_t *) srcp)[0];
srcp -= -1 * OPSIZ;
dstp -= 0 * OPSIZ;
len -= 1;
if (OP_T_THRES <= 3 * OPSIZ && len == 0)
goto do0;
goto do8; /* No-op. */
}
do {
do8:
a0 = ((op_t *) srcp)[0];
((op_t *) dstp)[0] = a1;
do7:
a1 = ((op_t *) srcp)[1];
((op_t *) dstp)[1] = a0;
do6:
a0 = ((op_t *) srcp)[2];
((op_t *) dstp)[2] = a1;
do5:
a1 = ((op_t *) srcp)[3];
((op_t *) dstp)[3] = a0;
do4:
a0 = ((op_t *) srcp)[4];
((op_t *) dstp)[4] = a1;
do3:
a1 = ((op_t *) srcp)[5];
((op_t *) dstp)[5] = a0;
do2:
a0 = ((op_t *) srcp)[6];
((op_t *) dstp)[6] = a1;
do1:
a1 = ((op_t *) srcp)[7];
((op_t *) dstp)[7] = a0;
srcp += 8 * OPSIZ;
dstp += 8 * OPSIZ;
len -= 8;
} while (len != 0);
/* This is the right position for do0. Please don't move
* it into the loop.
*/
do0:
((op_t *) dstp)[0] = a1;
}
static void _wordcopy_fwd_dest_aligned(long dstp, long srcp, size_t len)
{
op_t a0, a1, a2, a3;
int sh_1, sh_2;
/* Calculate how to shift a word read at the memory operation
* aligned srcp to make it aligned for copy.
*/
sh_1 = 8 * (srcp % OPSIZ);
sh_2 = 8 * OPSIZ - sh_1;
/* Make SRCP aligned by rounding it down to the beginning of the `op_t'
* it points in the middle of.
*/
srcp &= -OPSIZ;
switch (len % 4) {
case 2:
a1 = ((op_t *) srcp)[0];
a2 = ((op_t *) srcp)[1];
srcp -= 1 * OPSIZ;
dstp -= 3 * OPSIZ;
len += 2;
goto do1;
case 3:
a0 = ((op_t *) srcp)[0];
a1 = ((op_t *) srcp)[1];
srcp -= 0 * OPSIZ;
dstp -= 2 * OPSIZ;
len += 1;
goto do2;
case 0:
if (OP_T_THRES <= 3 * OPSIZ && len == 0)
return;
a3 = ((op_t *) srcp)[0];
a0 = ((op_t *) srcp)[1];
srcp -= -1 * OPSIZ;
dstp -= 1 * OPSIZ;
len += 0;
goto do3;
case 1:
a2 = ((op_t *) srcp)[0];
a3 = ((op_t *) srcp)[1];
srcp -= -2 * OPSIZ;
dstp -= 0 * OPSIZ;
len -= 1;
if (OP_T_THRES <= 3 * OPSIZ && len == 0)
goto do0;
goto do4; /* No-op. */
}
do {
do4:
a0 = ((op_t *) srcp)[0];
((op_t *) dstp)[0] = MERGE(a2, sh_1, a3, sh_2);
do3:
a1 = ((op_t *) srcp)[1];
((op_t *) dstp)[1] = MERGE(a3, sh_1, a0, sh_2);
do2:
a2 = ((op_t *) srcp)[2];
((op_t *) dstp)[2] = MERGE(a0, sh_1, a1, sh_2);
do1:
a3 = ((op_t *) srcp)[3];
((op_t *) dstp)[3] = MERGE(a1, sh_1, a2, sh_2);
srcp += 4 * OPSIZ;
dstp += 4 * OPSIZ;
len -= 4;
} while (len != 0);
/* This is the right position for do0. Please don't move
* it into the loop.
*/
do0:
((op_t *) dstp)[0] = MERGE(a2, sh_1, a3, sh_2);
}
#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes) \
do { \
size_t __nbytes = (nbytes); \
while (__nbytes > 0) { \
byte __x = ((byte *) src_bp)[0]; \
src_bp += 1; \
__nbytes -= 1; \
((byte *) dst_bp)[0] = __x; \
dst_bp += 1; \
} \
} while (0)
#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes) \
do { \
if (src_bp % OPSIZ == 0) \
_wordcopy_fwd_aligned(dst_bp, src_bp, (nbytes) / OPSIZ); \
else \
_wordcopy_fwd_dest_aligned(dst_bp, src_bp, (nbytes) / OPSIZ); \
src_bp += (nbytes) & -OPSIZ; \
dst_bp += (nbytes) & -OPSIZ; \
(nbytes_left) = (nbytes) % OPSIZ; \
} while (0)
extern void *__memcpy_aligned(void *dest, const void *src, size_t len);
void *__memcpy(void *dest, const void *src, size_t len)
{
unsigned long dstp = (long) dest;
unsigned long srcp = (long) src;
/* If there not too few bytes to copy, use word copy. */
if (len >= OP_T_THRES) {
if ((len >= FAST_COPY_THRES) && ((dstp & OPSIZ_MASK) == 0) &&
((srcp & OPSIZ_MASK) == 0)) {
__memcpy_aligned(dest, src, len);
return dest;
}
/* Copy just a few bytes to make DSTP aligned. */
len -= (-dstp) % OPSIZ;
BYTE_COPY_FWD(dstp, srcp, (-dstp) % OPSIZ);
/* Copy from SRCP to DSTP taking advantage of the known alignment of
* DSTP. Number of bytes remaining is put in the third argument,
* i.e. in LEN. This number may vary from machine to machine.
*/
WORD_COPY_FWD(dstp, srcp, len, len);
/* Fall out and copy the tail. */
}
/* There are just a few bytes to copy. Use byte memory operations. */
BYTE_COPY_FWD(dstp, srcp, len);
return dest;
}
EXPORT_SYMBOL(__memcpy);
void *memcpy(void *dest, const void *src, size_t len) __weak __alias(__memcpy);
EXPORT_SYMBOL(memcpy);