[PATCH v2 5/8] mips: Add C implementation of memcpy/memset
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Wed Jun 18 21:00:44 GMT 2025
On 18/06/25 11:33, Jovan Dmitrovic wrote:
> Add improved C implementation of memcpy/memset and remove corresponding
> .S files.
>
> Cherry-picked 6b74133706246af94b71e4154e4ca09482828c9f
> from https://github.com/MIPS/glibc
>
> Signed-off-by: Faraz Shahbazker <fshahbazker@wavecomp.com>
> Signed-off-by: Aleksandar Rakic <aleksandar.rakic@htecgroup.com>
> Signed-off-by: Jovan Dmitrović <jovan.dmitrovic@htecgroup.com>
This build fails with:
[...]/build/mipsel-linux-gnu/string/memcpy.o
../sysdeps/mips/memcpy.c:78:6: error: "__mips_isa_rev" is not defined, evaluates to 0 [-Werror=undef]
78 | #if (__mips_isa_rev < 6 && !defined(__mips1)) || defined(__nanomips__)
| ^~~~~~~~~~~~~~
../sysdeps/mips/memcpy.c:86:6: error: "__mips_isa_rev" is not defined, evaluates to 0 [-Werror=undef]
86 | #if __mips_isa_rev >= 6 && !defined(__nanomips__)
| ^~~~~~~~~~~~~~
cc1: all warnings being treated as errors
You need to include <sysdep.h>.
> ---
> sysdeps/mips/memcpy.S | 886 ------------------------------------------
> sysdeps/mips/memcpy.c | 415 ++++++++++++++++++++
> sysdeps/mips/memset.S | 430 --------------------
> sysdeps/mips/memset.c | 188 +++++++++
> 4 files changed, 603 insertions(+), 1316 deletions(-)
> delete mode 100644 sysdeps/mips/memcpy.S
> create mode 100644 sysdeps/mips/memcpy.c
> delete mode 100644 sysdeps/mips/memset.S
> create mode 100644 sysdeps/mips/memset.c
>
> diff --git a/sysdeps/mips/memcpy.S b/sysdeps/mips/memcpy.S
> deleted file mode 100644
> index 8736cfbec6..0000000000
> --- a/sysdeps/mips/memcpy.S
> +++ /dev/null
> @@ -1,886 +0,0 @@
> -/* Copyright (C) 2012-2025 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
> - <https://www.gnu.org/licenses/>. */
> -
> -#ifdef ANDROID_CHANGES
> -# include "machine/asm.h"
> -# include "machine/regdef.h"
> -# define USE_MEMMOVE_FOR_OVERLAP
> -# define PREFETCH_LOAD_HINT PREFETCH_HINT_LOAD_STREAMED
> -# define PREFETCH_STORE_HINT PREFETCH_HINT_PREPAREFORSTORE
> -#elif _LIBC
> -# include <sysdep.h>
> -# include <regdef.h>
> -# include <sys/asm.h>
> -# define PREFETCH_LOAD_HINT PREFETCH_HINT_LOAD_STREAMED
> -# define PREFETCH_STORE_HINT PREFETCH_HINT_PREPAREFORSTORE
> -#elif defined _COMPILING_NEWLIB
> -# include "machine/asm.h"
> -# include "machine/regdef.h"
> -# define PREFETCH_LOAD_HINT PREFETCH_HINT_LOAD_STREAMED
> -# define PREFETCH_STORE_HINT PREFETCH_HINT_PREPAREFORSTORE
> -#else
> -# include <regdef.h>
> -# include <sys/asm.h>
> -#endif
> -
> -#if (_MIPS_ISA == _MIPS_ISA_MIPS4) || (_MIPS_ISA == _MIPS_ISA_MIPS5) || \
> - (_MIPS_ISA == _MIPS_ISA_MIPS32) || (_MIPS_ISA == _MIPS_ISA_MIPS64)
> -# ifndef DISABLE_PREFETCH
> -# define USE_PREFETCH
> -# endif
> -#endif
> -
> -#if defined(_MIPS_SIM) && ((_MIPS_SIM == _ABI64) || (_MIPS_SIM == _ABIN32))
> -# ifndef DISABLE_DOUBLE
> -# define USE_DOUBLE
> -# endif
> -#endif
> -
> -/* Some asm.h files do not have the L macro definition. */
> -#ifndef L
> -# if _MIPS_SIM == _ABIO32
> -# define L(label) $L ## label
> -# else
> -# define L(label) .L ## label
> -# endif
> -#endif
> -
> -/* Some asm.h files do not have the PTR_ADDIU macro definition. */
> -#ifndef PTR_ADDIU
> -# ifdef USE_DOUBLE
> -# define PTR_ADDIU daddiu
> -# else
> -# define PTR_ADDIU addiu
> -# endif
> -#endif
> -
> -/* Some asm.h files do not have the PTR_SRA macro definition. */
> -#ifndef PTR_SRA
> -# ifdef USE_DOUBLE
> -# define PTR_SRA dsra
> -# else
> -# define PTR_SRA sra
> -# endif
> -#endif
> -
> -/* New R6 instructions that may not be in asm.h. */
> -#ifndef PTR_LSA
> -# if _MIPS_SIM == _ABI64
> -# define PTR_LSA dlsa
> -# else
> -# define PTR_LSA lsa
> -# endif
> -#endif
> -
> -#if __mips_isa_rev > 5 && defined (__mips_micromips)
> -# define PTR_BC bc16
> -#else
> -# define PTR_BC bc
> -#endif
> -
> -/*
> - * Using PREFETCH_HINT_LOAD_STREAMED instead of PREFETCH_LOAD on load
> - * prefetches appear to offer a slight performance advantage.
> - *
> - * Using PREFETCH_HINT_PREPAREFORSTORE instead of PREFETCH_STORE
> - * or PREFETCH_STORE_STREAMED offers a large performance advantage
> - * but PREPAREFORSTORE has some special restrictions to consider.
> - *
> - * Prefetch with the 'prepare for store' hint does not copy a memory
> - * location into the cache, it just allocates a cache line and zeros
> - * it out. This means that if you do not write to the entire cache
> - * line before writing it out to memory some data will get zero'ed out
> - * when the cache line is written back to memory and data will be lost.
> - *
> - * Also if you are using this memcpy to copy overlapping buffers it may
> - * not behave correctly when using the 'prepare for store' hint. If you
> - * use the 'prepare for store' prefetch on a memory area that is in the
> - * memcpy source (as well as the memcpy destination), then you will get
> - * some data zero'ed out before you have a chance to read it and data will
> - * be lost.
> - *
> - * If you are going to use this memcpy routine with the 'prepare for store'
> - * prefetch you may want to set USE_MEMMOVE_FOR_OVERLAP in order to avoid
> - * the problem of running memcpy on overlapping buffers.
> - *
> - * There are ifdef'ed sections of this memcpy to make sure that it does not
> - * do prefetches on cache lines that are not going to be completely written.
> - * This code is only needed and only used when PREFETCH_STORE_HINT is set to
> - * PREFETCH_HINT_PREPAREFORSTORE. This code assumes that cache lines are
> - * 32 bytes and if the cache line is larger it will not work correctly.
> - */
> -
> -#ifdef USE_PREFETCH
> -# define PREFETCH_HINT_LOAD 0
> -# define PREFETCH_HINT_STORE 1
> -# define PREFETCH_HINT_LOAD_STREAMED 4
> -# define PREFETCH_HINT_STORE_STREAMED 5
> -# define PREFETCH_HINT_LOAD_RETAINED 6
> -# define PREFETCH_HINT_STORE_RETAINED 7
> -# define PREFETCH_HINT_WRITEBACK_INVAL 25
> -# define PREFETCH_HINT_PREPAREFORSTORE 30
> -
> -/*
> - * If we have not picked out what hints to use at this point use the
> - * standard load and store prefetch hints.
> - */
> -# ifndef PREFETCH_STORE_HINT
> -# define PREFETCH_STORE_HINT PREFETCH_HINT_STORE
> -# endif
> -# ifndef PREFETCH_LOAD_HINT
> -# define PREFETCH_LOAD_HINT PREFETCH_HINT_LOAD
> -# endif
> -
> -/*
> - * We double everything when USE_DOUBLE is true so we do 2 prefetches to
> - * get 64 bytes in that case. The assumption is that each individual
> - * prefetch brings in 32 bytes.
> - */
> -
> -# ifdef USE_DOUBLE
> -# define PREFETCH_CHUNK 64
> -# define PREFETCH_FOR_LOAD(chunk, reg) \
> - pref PREFETCH_LOAD_HINT, (chunk)*64(reg); \
> - pref PREFETCH_LOAD_HINT, ((chunk)*64)+32(reg)
> -# define PREFETCH_FOR_STORE(chunk, reg) \
> - pref PREFETCH_STORE_HINT, (chunk)*64(reg); \
> - pref PREFETCH_STORE_HINT, ((chunk)*64)+32(reg)
> -# else
> -# define PREFETCH_CHUNK 32
> -# define PREFETCH_FOR_LOAD(chunk, reg) \
> - pref PREFETCH_LOAD_HINT, (chunk)*32(reg)
> -# define PREFETCH_FOR_STORE(chunk, reg) \
> - pref PREFETCH_STORE_HINT, (chunk)*32(reg)
> -# endif
> -/* MAX_PREFETCH_SIZE is the maximum size of a prefetch, it must not be less
> - * than PREFETCH_CHUNK, the assumed size of each prefetch. If the real size
> - * of a prefetch is greater than MAX_PREFETCH_SIZE and the PREPAREFORSTORE
> - * hint is used, the code will not work correctly. If PREPAREFORSTORE is not
> - * used then MAX_PREFETCH_SIZE does not matter. */
> -# define MAX_PREFETCH_SIZE 128
> -/* PREFETCH_LIMIT is set based on the fact that we never use an offset greater
> - * than 5 on a STORE prefetch and that a single prefetch can never be larger
> - * than MAX_PREFETCH_SIZE. We add the extra 32 when USE_DOUBLE is set because
> - * we actually do two prefetches in that case, one 32 bytes after the other. */
> -# ifdef USE_DOUBLE
> -# define PREFETCH_LIMIT (5 * PREFETCH_CHUNK) + 32 + MAX_PREFETCH_SIZE
> -# else
> -# define PREFETCH_LIMIT (5 * PREFETCH_CHUNK) + MAX_PREFETCH_SIZE
> -# endif
> -# if (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE) \
> - && ((PREFETCH_CHUNK * 4) < MAX_PREFETCH_SIZE)
> -/* We cannot handle this because the initial prefetches may fetch bytes that
> - * are before the buffer being copied. We start copies with an offset
> - * of 4 so avoid this situation when using PREPAREFORSTORE. */
> -#error "PREFETCH_CHUNK is too large and/or MAX_PREFETCH_SIZE is too small."
> -# endif
> -#else /* USE_PREFETCH not defined */
> -# define PREFETCH_FOR_LOAD(offset, reg)
> -# define PREFETCH_FOR_STORE(offset, reg)
> -#endif
> -
> -#if __mips_isa_rev > 5
> -# if (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
> -# undef PREFETCH_STORE_HINT
> -# define PREFETCH_STORE_HINT PREFETCH_HINT_STORE_STREAMED
> -# endif
> -# define R6_CODE
> -#endif
> -
> -/* Allow the routine to be named something else if desired. */
> -#ifndef MEMCPY_NAME
> -# define MEMCPY_NAME memcpy
> -#endif
> -
> -/* We use these 32/64 bit registers as temporaries to do the copying. */
> -#define REG0 t0
> -#define REG1 t1
> -#define REG2 t2
> -#define REG3 t3
> -#if defined(_MIPS_SIM) && ((_MIPS_SIM == _ABIO32) || (_MIPS_SIM == _ABIO64))
> -# define REG4 t4
> -# define REG5 t5
> -# define REG6 t6
> -# define REG7 t7
> -#else
> -# define REG4 ta0
> -# define REG5 ta1
> -# define REG6 ta2
> -# define REG7 ta3
> -#endif
> -
> -/* We load/store 64 bits at a time when USE_DOUBLE is true.
> - * The C_ prefix stands for CHUNK and is used to avoid macro name
> - * conflicts with system header files. */
> -
> -#ifdef USE_DOUBLE
> -# define C_ST sd
> -# define C_LD ld
> -# ifdef __MIPSEB
> -# define C_LDHI ldl /* high part is left in big-endian */
> -# define C_STHI sdl /* high part is left in big-endian */
> -# define C_LDLO ldr /* low part is right in big-endian */
> -# define C_STLO sdr /* low part is right in big-endian */
> -# else
> -# define C_LDHI ldr /* high part is right in little-endian */
> -# define C_STHI sdr /* high part is right in little-endian */
> -# define C_LDLO ldl /* low part is left in little-endian */
> -# define C_STLO sdl /* low part is left in little-endian */
> -# endif
> -# define C_ALIGN dalign /* r6 align instruction */
> -#else
> -# define C_ST sw
> -# define C_LD lw
> -# ifdef __MIPSEB
> -# define C_LDHI lwl /* high part is left in big-endian */
> -# define C_STHI swl /* high part is left in big-endian */
> -# define C_LDLO lwr /* low part is right in big-endian */
> -# define C_STLO swr /* low part is right in big-endian */
> -# else
> -# define C_LDHI lwr /* high part is right in little-endian */
> -# define C_STHI swr /* high part is right in little-endian */
> -# define C_LDLO lwl /* low part is left in little-endian */
> -# define C_STLO swl /* low part is left in little-endian */
> -# endif
> -# define C_ALIGN align /* r6 align instruction */
> -#endif
> -
> -/* Bookkeeping values for 32 vs. 64 bit mode. */
> -#ifdef USE_DOUBLE
> -# define NSIZE 8
> -# define NSIZEMASK 0x3f
> -# define NSIZEDMASK 0x7f
> -#else
> -# define NSIZE 4
> -# define NSIZEMASK 0x1f
> -# define NSIZEDMASK 0x3f
> -#endif
> -#define UNIT(unit) ((unit)*NSIZE)
> -#define UNITM1(unit) (((unit)*NSIZE)-1)
> -
> -#ifdef ANDROID_CHANGES
> -LEAF(MEMCPY_NAME, 0)
> -#else
> -LEAF(MEMCPY_NAME)
> -#endif
> - .set nomips16
> -/*
> - * Below we handle the case where memcpy is called with overlapping src and dst.
> - * Although memcpy is not required to handle this case, some parts of Android
> - * like Skia rely on such usage. We call memmove to handle such cases.
> - */
> -#ifdef USE_MEMMOVE_FOR_OVERLAP
> - PTR_SUBU t0,a0,a1
> - PTR_SRA t2,t0,31
> - xor t1,t0,t2
> - PTR_SUBU t0,t1,t2
> - sltu t2,t0,a2
> - la t9,memmove
> - beq t2,zero,L(memcpy)
> - jr t9
> -L(memcpy):
> -#endif
> -/*
> - * If the size is less than 2*NSIZE (8 or 16), go to L(lastb). Regardless of
> - * size, copy dst pointer to v0 for the return value.
> - */
> - slti t2,a2,(2 * NSIZE)
> -#if defined(RETURN_FIRST_PREFETCH) || defined(RETURN_LAST_PREFETCH)
> - move v0,zero
> -#else
> - move v0,a0
> -#endif
> - bne t2,zero,L(lasts)
> -
> -#ifndef R6_CODE
> -
> -/*
> - * If src and dst have different alignments, go to L(unaligned), if they
> - * have the same alignment (but are not actually aligned) do a partial
> - * load/store to make them aligned. If they are both already aligned
> - * we can start copying at L(aligned).
> - */
> - xor t8,a1,a0
> - andi t8,t8,(NSIZE-1) /* t8 is a0/a1 word-displacement */
> - PTR_SUBU a3, zero, a0
> - bne t8,zero,L(unaligned)
> -
> - andi a3,a3,(NSIZE-1) /* copy a3 bytes to align a0/a1 */
> - PTR_SUBU a2,a2,a3 /* a2 is the remining bytes count */
> - beq a3,zero,L(aligned) /* if a3=0, it is already aligned */
> -
> - C_LDHI t8,0(a1)
> - PTR_ADDU a1,a1,a3
> - C_STHI t8,0(a0)
> - PTR_ADDU a0,a0,a3
> -
> -#else /* R6_CODE */
> -
> -/*
> - * Align the destination and hope that the source gets aligned too. If it
> - * doesn't we jump to L(r6_unaligned*) to do unaligned copies using the r6
> - * align instruction.
> - */
> - andi t8,a0,7
> -#ifdef __mips_micromips
> - auipc t9,%pcrel_hi(L(atable))
> - addiu t9,t9,%pcrel_lo(L(atable)+4)
> - PTR_LSA t9,t8,t9,1
> -#else
> - lapc t9,L(atable)
> - PTR_LSA t9,t8,t9,2
> -#endif
> - jrc t9
> -L(atable):
> - PTR_BC L(lb0)
> - PTR_BC L(lb7)
> - PTR_BC L(lb6)
> - PTR_BC L(lb5)
> - PTR_BC L(lb4)
> - PTR_BC L(lb3)
> - PTR_BC L(lb2)
> - PTR_BC L(lb1)
> -L(lb7):
> - lb a3, 6(a1)
> - sb a3, 6(a0)
> -L(lb6):
> - lb a3, 5(a1)
> - sb a3, 5(a0)
> -L(lb5):
> - lb a3, 4(a1)
> - sb a3, 4(a0)
> -L(lb4):
> - lb a3, 3(a1)
> - sb a3, 3(a0)
> -L(lb3):
> - lb a3, 2(a1)
> - sb a3, 2(a0)
> -L(lb2):
> - lb a3, 1(a1)
> - sb a3, 1(a0)
> -L(lb1):
> - lb a3, 0(a1)
> - sb a3, 0(a0)
> -
> - li t9,8
> - subu t8,t9,t8
> - PTR_SUBU a2,a2,t8
> - PTR_ADDU a0,a0,t8
> - PTR_ADDU a1,a1,t8
> -L(lb0):
> -
> - andi t8,a1,(NSIZE-1)
> -#ifdef __mips_micromips
> - auipc t9,%pcrel_hi(L(jtable))
> - addiu t9,t9,%pcrel_lo(L(jtable)+4)
> - PTR_LSA t9,t8,t9,1
> -#else
> - lapc t9,L(jtable)
> - PTR_LSA t9,t8,t9,2
> -#endif
> - jrc t9
> -L(jtable):
> - PTR_BC L(aligned)
> - PTR_BC L(r6_unaligned1)
> - PTR_BC L(r6_unaligned2)
> - PTR_BC L(r6_unaligned3)
> -#ifdef USE_DOUBLE
> - PTR_BC L(r6_unaligned4)
> - PTR_BC L(r6_unaligned5)
> - PTR_BC L(r6_unaligned6)
> - PTR_BC L(r6_unaligned7)
> -#endif
> -#endif /* R6_CODE */
> -
> -L(aligned):
> -
> -/*
> - * Now dst/src are both aligned to (word or double word) aligned addresses
> - * Set a2 to count how many bytes we have to copy after all the 64/128 byte
> - * chunks are copied and a3 to the dst pointer after all the 64/128 byte
> - * chunks have been copied. We will loop, incrementing a0 and a1 until a0
> - * equals a3.
> - */
> -
> - andi t8,a2,NSIZEDMASK /* any whole 64-byte/128-byte chunks? */
> - PTR_SUBU a3,a2,t8 /* subtract from a2 the reminder */
> - beq a2,t8,L(chkw) /* if a2==t8, no 64-byte/128-byte chunks */
> - PTR_ADDU a3,a0,a3 /* Now a3 is the final dst after loop */
> -
> -/* When in the loop we may prefetch with the 'prepare to store' hint,
> - * in this case the a0+x should not be past the "t0-32" address. This
> - * means: for x=128 the last "safe" a0 address is "t0-160". Alternatively,
> - * for x=64 the last "safe" a0 address is "t0-96" In the current version we
> - * will use "prefetch hint,128(a0)", so "t0-160" is the limit.
> - */
> -#if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
> - PTR_ADDU t0,a0,a2 /* t0 is the "past the end" address */
> - PTR_SUBU t9,t0,PREFETCH_LIMIT /* t9 is the "last safe pref" address */
> -#endif
> - PREFETCH_FOR_LOAD (0, a1)
> - PREFETCH_FOR_LOAD (1, a1)
> - PREFETCH_FOR_LOAD (2, a1)
> - PREFETCH_FOR_LOAD (3, a1)
> -#if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT != PREFETCH_HINT_PREPAREFORSTORE)
> - PREFETCH_FOR_STORE (1, a0)
> - PREFETCH_FOR_STORE (2, a0)
> - PREFETCH_FOR_STORE (3, a0)
> -#endif
> -#if defined(RETURN_FIRST_PREFETCH) && defined(USE_PREFETCH)
> -# if PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE
> - sltu v1,t9,a0
> - bgtz v1,L(skip_set)
> - PTR_ADDIU v0,a0,(PREFETCH_CHUNK*4)
> -L(skip_set):
> -# else
> - PTR_ADDIU v0,a0,(PREFETCH_CHUNK*1)
> -# endif
> -#endif
> -#if defined(RETURN_LAST_PREFETCH) && defined(USE_PREFETCH) \
> - && (PREFETCH_STORE_HINT != PREFETCH_HINT_PREPAREFORSTORE)
> - PTR_ADDIU v0,a0,(PREFETCH_CHUNK*3)
> -# ifdef USE_DOUBLE
> - PTR_ADDIU v0,v0,32
> -# endif
> -#endif
> -L(loop16w):
> - C_LD t0,UNIT(0)(a1)
> -/* We need to separate out the C_LD instruction here so that it will work
> - both when it is used by itself and when it is used with the branch
> - instruction. */
> -#if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
> - sltu v1,t9,a0 /* If a0 > t9 don't use next prefetch */
> - C_LD t1,UNIT(1)(a1)
> - bgtz v1,L(skip_pref)
> -#else
> - C_LD t1,UNIT(1)(a1)
> -#endif
> -#ifdef R6_CODE
> - PREFETCH_FOR_STORE (2, a0)
> -#else
> - PREFETCH_FOR_STORE (4, a0)
> - PREFETCH_FOR_STORE (5, a0)
> -#endif
> -#if defined(RETURN_LAST_PREFETCH) && defined(USE_PREFETCH)
> - PTR_ADDIU v0,a0,(PREFETCH_CHUNK*5)
> -# ifdef USE_DOUBLE
> - PTR_ADDIU v0,v0,32
> -# endif
> -#endif
> -L(skip_pref):
> - C_LD REG2,UNIT(2)(a1)
> - C_LD REG3,UNIT(3)(a1)
> - C_LD REG4,UNIT(4)(a1)
> - C_LD REG5,UNIT(5)(a1)
> - C_LD REG6,UNIT(6)(a1)
> - C_LD REG7,UNIT(7)(a1)
> -#ifdef R6_CODE
> - PREFETCH_FOR_LOAD (3, a1)
> -#else
> - PREFETCH_FOR_LOAD (4, a1)
> -#endif
> - C_ST t0,UNIT(0)(a0)
> - C_ST t1,UNIT(1)(a0)
> - C_ST REG2,UNIT(2)(a0)
> - C_ST REG3,UNIT(3)(a0)
> - C_ST REG4,UNIT(4)(a0)
> - C_ST REG5,UNIT(5)(a0)
> - C_ST REG6,UNIT(6)(a0)
> - C_ST REG7,UNIT(7)(a0)
> -
> - C_LD t0,UNIT(8)(a1)
> - C_LD t1,UNIT(9)(a1)
> - C_LD REG2,UNIT(10)(a1)
> - C_LD REG3,UNIT(11)(a1)
> - C_LD REG4,UNIT(12)(a1)
> - C_LD REG5,UNIT(13)(a1)
> - C_LD REG6,UNIT(14)(a1)
> - C_LD REG7,UNIT(15)(a1)
> -#ifndef R6_CODE
> - PREFETCH_FOR_LOAD (5, a1)
> -#endif
> - C_ST t0,UNIT(8)(a0)
> - C_ST t1,UNIT(9)(a0)
> - C_ST REG2,UNIT(10)(a0)
> - C_ST REG3,UNIT(11)(a0)
> - C_ST REG4,UNIT(12)(a0)
> - C_ST REG5,UNIT(13)(a0)
> - C_ST REG6,UNIT(14)(a0)
> - C_ST REG7,UNIT(15)(a0)
> - PTR_ADDIU a0,a0,UNIT(16) /* adding 64/128 to dest */
> - PTR_ADDIU a1,a1,UNIT(16) /* adding 64/128 to src */
> - bne a0,a3,L(loop16w)
> - move a2,t8
> -
> -/* Here we have src and dest word-aligned but less than 64-bytes or
> - * 128 bytes to go. Check for a 32(64) byte chunk and copy if there
> - * is one. Otherwise jump down to L(chk1w) to handle the tail end of
> - * the copy.
> - */
> -
> -L(chkw):
> - PREFETCH_FOR_LOAD (0, a1)
> - andi t8,a2,NSIZEMASK /* Is there a 32-byte/64-byte chunk. */
> - /* The t8 is the reminder count past 32-bytes */
> - beq a2,t8,L(chk1w) /* When a2=t8, no 32-byte chunk */
> - C_LD t0,UNIT(0)(a1)
> - C_LD t1,UNIT(1)(a1)
> - C_LD REG2,UNIT(2)(a1)
> - C_LD REG3,UNIT(3)(a1)
> - C_LD REG4,UNIT(4)(a1)
> - C_LD REG5,UNIT(5)(a1)
> - C_LD REG6,UNIT(6)(a1)
> - C_LD REG7,UNIT(7)(a1)
> - PTR_ADDIU a1,a1,UNIT(8)
> - C_ST t0,UNIT(0)(a0)
> - C_ST t1,UNIT(1)(a0)
> - C_ST REG2,UNIT(2)(a0)
> - C_ST REG3,UNIT(3)(a0)
> - C_ST REG4,UNIT(4)(a0)
> - C_ST REG5,UNIT(5)(a0)
> - C_ST REG6,UNIT(6)(a0)
> - C_ST REG7,UNIT(7)(a0)
> - PTR_ADDIU a0,a0,UNIT(8)
> -
> -/*
> - * Here we have less than 32(64) bytes to copy. Set up for a loop to
> - * copy one word (or double word) at a time. Set a2 to count how many
> - * bytes we have to copy after all the word (or double word) chunks are
> - * copied and a3 to the dst pointer after all the (d)word chunks have
> - * been copied. We will loop, incrementing a0 and a1 until a0 equals a3.
> - */
> -L(chk1w):
> - andi a2,t8,(NSIZE-1) /* a2 is the reminder past one (d)word chunks */
> - PTR_SUBU a3,t8,a2 /* a3 is count of bytes in one (d)word chunks */
> - beq a2,t8,L(lastw)
> - PTR_ADDU a3,a0,a3 /* a3 is the dst address after loop */
> -
> -/* copying in words (4-byte or 8-byte chunks) */
> -L(wordCopy_loop):
> - C_LD REG3,UNIT(0)(a1)
> - PTR_ADDIU a0,a0,UNIT(1)
> - PTR_ADDIU a1,a1,UNIT(1)
> - C_ST REG3,UNIT(-1)(a0)
> - bne a0,a3,L(wordCopy_loop)
> -
> -/* If we have been copying double words, see if we can copy a single word
> - before doing byte copies. We can have, at most, one word to copy. */
> -
> -L(lastw):
> -#ifdef USE_DOUBLE
> - andi t8,a2,3 /* a2 is the remainder past 4 byte chunks. */
> - beq t8,a2,L(lastb)
> - move a2,t8
> - lw REG3,0(a1)
> - sw REG3,0(a0)
> - PTR_ADDIU a0,a0,4
> - PTR_ADDIU a1,a1,4
> -#endif
> -
> -/* Copy the last 8 (or 16) bytes */
> -L(lastb):
> - PTR_ADDU a3,a0,a2 /* a3 is the last dst address */
> - blez a2,L(leave)
> -L(lastbloop):
> - lb v1,0(a1)
> - PTR_ADDIU a0,a0,1
> - PTR_ADDIU a1,a1,1
> - sb v1,-1(a0)
> - bne a0,a3,L(lastbloop)
> -L(leave):
> - jr ra
> -
> -/* We jump here with a memcpy of less than 8 or 16 bytes, depending on
> - whether or not USE_DOUBLE is defined. Instead of just doing byte
> - copies, check the alignment and size and use lw/sw if possible.
> - Otherwise, do byte copies. */
> -
> -L(lasts):
> - andi t8,a2,3
> - beq t8,a2,L(lastb)
> -
> - andi t9,a0,3
> - bne t9,zero,L(lastb)
> - andi t9,a1,3
> - bne t9,zero,L(lastb)
> -
> - PTR_SUBU a3,a2,t8
> - PTR_ADDU a3,a0,a3
> -
> -L(wcopy_loop):
> - lw REG3,0(a1)
> - PTR_ADDIU a0,a0,4
> - PTR_ADDIU a1,a1,4
> - bne a0,a3,L(wcopy_loop)
> - sw REG3,-4(a0)
> -
> - b L(lastb)
> - move a2,t8
> -
> -#ifndef R6_CODE
> -/*
> - * UNALIGNED case, got here with a3 = "negu a0"
> - * This code is nearly identical to the aligned code above
> - * but only the destination (not the source) gets aligned
> - * so we need to do partial loads of the source followed
> - * by normal stores to the destination (once we have aligned
> - * the destination).
> - */
> -
> -L(unaligned):
> - andi a3,a3,(NSIZE-1) /* copy a3 bytes to align a0/a1 */
> - PTR_SUBU a2,a2,a3 /* a2 is the remining bytes count */
> - beqz a3,L(ua_chk16w) /* if a3=0, it is already aligned */
> -
> - C_LDHI v1,UNIT(0)(a1)
> - C_LDLO v1,UNITM1(1)(a1)
> - PTR_ADDU a1,a1,a3
> - C_STHI v1,UNIT(0)(a0)
> - PTR_ADDU a0,a0,a3
> -
> -/*
> - * Now the destination (but not the source) is aligned
> - * Set a2 to count how many bytes we have to copy after all the 64/128 byte
> - * chunks are copied and a3 to the dst pointer after all the 64/128 byte
> - * chunks have been copied. We will loop, incrementing a0 and a1 until a0
> - * equals a3.
> - */
> -
> -L(ua_chk16w):
> - andi t8,a2,NSIZEDMASK /* any whole 64-byte/128-byte chunks? */
> - PTR_SUBU a3,a2,t8 /* subtract from a2 the reminder */
> - beq a2,t8,L(ua_chkw) /* if a2==t8, no 64-byte/128-byte chunks */
> - PTR_ADDU a3,a0,a3 /* Now a3 is the final dst after loop */
> -
> -# if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
> - PTR_ADDU t0,a0,a2 /* t0 is the "past the end" address */
> - PTR_SUBU t9,t0,PREFETCH_LIMIT /* t9 is the "last safe pref" address */
> -# endif
> - PREFETCH_FOR_LOAD (0, a1)
> - PREFETCH_FOR_LOAD (1, a1)
> - PREFETCH_FOR_LOAD (2, a1)
> -# if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT != PREFETCH_HINT_PREPAREFORSTORE)
> - PREFETCH_FOR_STORE (1, a0)
> - PREFETCH_FOR_STORE (2, a0)
> - PREFETCH_FOR_STORE (3, a0)
> -# endif
> -# if defined(RETURN_FIRST_PREFETCH) && defined(USE_PREFETCH)
> -# if (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
> - sltu v1,t9,a0
> - bgtz v1,L(ua_skip_set)
> - PTR_ADDIU v0,a0,(PREFETCH_CHUNK*4)
> -L(ua_skip_set):
> -# else
> - PTR_ADDIU v0,a0,(PREFETCH_CHUNK*1)
> -# endif
> -# endif
> -L(ua_loop16w):
> - PREFETCH_FOR_LOAD (3, a1)
> - C_LDHI t0,UNIT(0)(a1)
> - C_LDHI t1,UNIT(1)(a1)
> - C_LDHI REG2,UNIT(2)(a1)
> -/* We need to separate out the C_LDHI instruction here so that it will work
> - both when it is used by itself and when it is used with the branch
> - instruction. */
> -# if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
> - sltu v1,t9,a0
> - C_LDHI REG3,UNIT(3)(a1)
> - bgtz v1,L(ua_skip_pref)
> -# else
> - C_LDHI REG3,UNIT(3)(a1)
> -# endif
> - PREFETCH_FOR_STORE (4, a0)
> - PREFETCH_FOR_STORE (5, a0)
> -L(ua_skip_pref):
> - C_LDHI REG4,UNIT(4)(a1)
> - C_LDHI REG5,UNIT(5)(a1)
> - C_LDHI REG6,UNIT(6)(a1)
> - C_LDHI REG7,UNIT(7)(a1)
> - C_LDLO t0,UNITM1(1)(a1)
> - C_LDLO t1,UNITM1(2)(a1)
> - C_LDLO REG2,UNITM1(3)(a1)
> - C_LDLO REG3,UNITM1(4)(a1)
> - C_LDLO REG4,UNITM1(5)(a1)
> - C_LDLO REG5,UNITM1(6)(a1)
> - C_LDLO REG6,UNITM1(7)(a1)
> - C_LDLO REG7,UNITM1(8)(a1)
> - PREFETCH_FOR_LOAD (4, a1)
> - C_ST t0,UNIT(0)(a0)
> - C_ST t1,UNIT(1)(a0)
> - C_ST REG2,UNIT(2)(a0)
> - C_ST REG3,UNIT(3)(a0)
> - C_ST REG4,UNIT(4)(a0)
> - C_ST REG5,UNIT(5)(a0)
> - C_ST REG6,UNIT(6)(a0)
> - C_ST REG7,UNIT(7)(a0)
> - C_LDHI t0,UNIT(8)(a1)
> - C_LDHI t1,UNIT(9)(a1)
> - C_LDHI REG2,UNIT(10)(a1)
> - C_LDHI REG3,UNIT(11)(a1)
> - C_LDHI REG4,UNIT(12)(a1)
> - C_LDHI REG5,UNIT(13)(a1)
> - C_LDHI REG6,UNIT(14)(a1)
> - C_LDHI REG7,UNIT(15)(a1)
> - C_LDLO t0,UNITM1(9)(a1)
> - C_LDLO t1,UNITM1(10)(a1)
> - C_LDLO REG2,UNITM1(11)(a1)
> - C_LDLO REG3,UNITM1(12)(a1)
> - C_LDLO REG4,UNITM1(13)(a1)
> - C_LDLO REG5,UNITM1(14)(a1)
> - C_LDLO REG6,UNITM1(15)(a1)
> - C_LDLO REG7,UNITM1(16)(a1)
> - PREFETCH_FOR_LOAD (5, a1)
> - C_ST t0,UNIT(8)(a0)
> - C_ST t1,UNIT(9)(a0)
> - C_ST REG2,UNIT(10)(a0)
> - C_ST REG3,UNIT(11)(a0)
> - C_ST REG4,UNIT(12)(a0)
> - C_ST REG5,UNIT(13)(a0)
> - C_ST REG6,UNIT(14)(a0)
> - C_ST REG7,UNIT(15)(a0)
> - PTR_ADDIU a0,a0,UNIT(16) /* adding 64/128 to dest */
> - PTR_ADDIU a1,a1,UNIT(16) /* adding 64/128 to src */
> - bne a0,a3,L(ua_loop16w)
> - move a2,t8
> -
> -/* Here we have src and dest word-aligned but less than 64-bytes or
> - * 128 bytes to go. Check for a 32(64) byte chunk and copy if there
> - * is one. Otherwise jump down to L(ua_chk1w) to handle the tail end of
> - * the copy. */
> -
> -L(ua_chkw):
> - PREFETCH_FOR_LOAD (0, a1)
> - andi t8,a2,NSIZEMASK /* Is there a 32-byte/64-byte chunk. */
> - /* t8 is the reminder count past 32-bytes */
> - beq a2,t8,L(ua_chk1w) /* When a2=t8, no 32-byte chunk */
> - C_LDHI t0,UNIT(0)(a1)
> - C_LDHI t1,UNIT(1)(a1)
> - C_LDHI REG2,UNIT(2)(a1)
> - C_LDHI REG3,UNIT(3)(a1)
> - C_LDHI REG4,UNIT(4)(a1)
> - C_LDHI REG5,UNIT(5)(a1)
> - C_LDHI REG6,UNIT(6)(a1)
> - C_LDHI REG7,UNIT(7)(a1)
> - C_LDLO t0,UNITM1(1)(a1)
> - C_LDLO t1,UNITM1(2)(a1)
> - C_LDLO REG2,UNITM1(3)(a1)
> - C_LDLO REG3,UNITM1(4)(a1)
> - C_LDLO REG4,UNITM1(5)(a1)
> - C_LDLO REG5,UNITM1(6)(a1)
> - C_LDLO REG6,UNITM1(7)(a1)
> - C_LDLO REG7,UNITM1(8)(a1)
> - PTR_ADDIU a1,a1,UNIT(8)
> - C_ST t0,UNIT(0)(a0)
> - C_ST t1,UNIT(1)(a0)
> - C_ST REG2,UNIT(2)(a0)
> - C_ST REG3,UNIT(3)(a0)
> - C_ST REG4,UNIT(4)(a0)
> - C_ST REG5,UNIT(5)(a0)
> - C_ST REG6,UNIT(6)(a0)
> - C_ST REG7,UNIT(7)(a0)
> - PTR_ADDIU a0,a0,UNIT(8)
> -/*
> - * Here we have less than 32(64) bytes to copy. Set up for a loop to
> - * copy one word (or double word) at a time.
> - */
> -L(ua_chk1w):
> - andi a2,t8,(NSIZE-1) /* a2 is the reminder past one (d)word chunks */
> - PTR_SUBU a3,t8,a2 /* a3 is count of bytes in one (d)word chunks */
> - beq a2,t8,L(ua_smallCopy)
> - PTR_ADDU a3,a0,a3 /* a3 is the dst address after loop */
> -
> -/* copying in words (4-byte or 8-byte chunks) */
> -L(ua_wordCopy_loop):
> - C_LDHI v1,UNIT(0)(a1)
> - C_LDLO v1,UNITM1(1)(a1)
> - PTR_ADDIU a0,a0,UNIT(1)
> - PTR_ADDIU a1,a1,UNIT(1)
> - C_ST v1,UNIT(-1)(a0)
> - bne a0,a3,L(ua_wordCopy_loop)
> -
> -/* Copy the last 8 (or 16) bytes */
> -L(ua_smallCopy):
> - PTR_ADDU a3,a0,a2 /* a3 is the last dst address */
> - beqz a2,L(leave)
> -L(ua_smallCopy_loop):
> - lb v1,0(a1)
> - PTR_ADDIU a0,a0,1
> - PTR_ADDIU a1,a1,1
> - sb v1,-1(a0)
> - bne a0,a3,L(ua_smallCopy_loop)
> -
> - jr ra
> -
> -#else /* R6_CODE */
> -
> -# ifdef __MIPSEB
> -# define SWAP_REGS(X,Y) X, Y
> -# define ALIGN_OFFSET(N) (N)
> -# else
> -# define SWAP_REGS(X,Y) Y, X
> -# define ALIGN_OFFSET(N) (NSIZE-N)
> -# endif
> -# define R6_UNALIGNED_WORD_COPY(BYTEOFFSET) \
> - andi REG7, a2, (NSIZE-1);/* REG7 is # of bytes to by bytes. */ \
> - PTR_SUBU a3, a2, REG7; /* a3 is number of bytes to be copied in */ \
> - /* (d)word chunks. */ \
> - beq REG7, a2, L(lastb); /* Check for bytes to copy by word */ \
> - move a2, REG7; /* a2 is # of bytes to copy byte by byte */ \
> - /* after word loop is finished. */ \
> - PTR_ADDU REG6, a0, a3; /* REG6 is the dst address after loop. */ \
> - PTR_SUBU REG2, a1, t8; /* REG2 is the aligned src address. */ \
> - PTR_ADDU a1, a1, a3; /* a1 is addr of source after word loop. */ \
> - C_LD t0, UNIT(0)(REG2); /* Load first part of source. */ \
> -L(r6_ua_wordcopy##BYTEOFFSET): \
> - C_LD t1, UNIT(1)(REG2); /* Load second part of source. */ \
> - C_ALIGN REG3, SWAP_REGS(t1,t0), ALIGN_OFFSET(BYTEOFFSET); \
> - PTR_ADDIU a0, a0, UNIT(1); /* Increment destination pointer. */ \
> - PTR_ADDIU REG2, REG2, UNIT(1); /* Increment aligned source pointer.*/ \
> - move t0, t1; /* Move second part of source to first. */ \
> - C_ST REG3, UNIT(-1)(a0); \
> - bne a0, REG6,L(r6_ua_wordcopy##BYTEOFFSET); \
> - j L(lastb); \
> -
> - /* We are generating R6 code, the destination is 4 byte aligned and
> - the source is not 4 byte aligned. t8 is 1, 2, or 3 depending on the
> - alignment of the source. */
> -
> -L(r6_unaligned1):
> - R6_UNALIGNED_WORD_COPY(1)
> -L(r6_unaligned2):
> - R6_UNALIGNED_WORD_COPY(2)
> -L(r6_unaligned3):
> - R6_UNALIGNED_WORD_COPY(3)
> -# ifdef USE_DOUBLE
> -L(r6_unaligned4):
> - R6_UNALIGNED_WORD_COPY(4)
> -L(r6_unaligned5):
> - R6_UNALIGNED_WORD_COPY(5)
> -L(r6_unaligned6):
> - R6_UNALIGNED_WORD_COPY(6)
> -L(r6_unaligned7):
> - R6_UNALIGNED_WORD_COPY(7)
> -# endif
> -#endif /* R6_CODE */
> -
> - .set at
> -END(MEMCPY_NAME)
> -#ifndef ANDROID_CHANGES
> -# ifdef _LIBC
> -libc_hidden_builtin_def (MEMCPY_NAME)
> -# endif
> -#endif
> diff --git a/sysdeps/mips/memcpy.c b/sysdeps/mips/memcpy.c
> new file mode 100644
> index 0000000000..8c3aec7b36
> --- /dev/null
> +++ b/sysdeps/mips/memcpy.c
> @@ -0,0 +1,415 @@
> +/*
> + * Copyright (C) 2024 MIPS Tech, LLC
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are met:
> + *
> + * 1. Redistributions of source code must retain the above copyright notice,
> + * this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright notice,
> + * this list of conditions and the following disclaimer in the documentation
> + * and/or other materials provided with the distribution.
> + * 3. Neither the name of the copyright holder nor the names of its
> + * contributors may be used to endorse or promote products derived from this
> + * software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> +*/
> +
> +#ifdef __GNUC__
> +
> +#undef memcpy
> +
> +/* Typical observed latency in cycles in fetching from DRAM. */
> +#define LATENCY_CYCLES 63
> +
> +/* Pre-fetch performance is subject to accurate prefetch ahead,
> + which in turn depends on both the cache-line size and the amount
> + of look-ahead. Since cache-line size is not nominally fixed in
> + a typically library built for multiple platforms, we make conservative
> + assumptions in the default case. This code will typically operate
> + on such conservative assumptions, but if compiled with the correct
> + -mtune=xx options, will perform even better on those specific
> + platforms. */
> +#if defined(_MIPS_TUNE_OCTEON2) || defined(_MIPS_TUNE_OCTEON3)
> + #define CACHE_LINE 128
> + #define BLOCK_CYCLES 30
> + #undef LATENCY_CYCLES
> + #define LATENCY_CYCLES 150
> +#elif defined(_MIPS_TUNE_I6400) || defined(_MIPS_TUNE_I6500)
> + #define CACHE_LINE 64
> + #define BLOCK_CYCLES 16
> +#elif defined(_MIPS_TUNE_P6600)
> + #define CACHE_LINE 32
> + #define BLOCK_CYCLES 12
> +#elif defined(_MIPS_TUNE_INTERAPTIV) || defined(_MIPS_TUNE_INTERAPTIV_MR2)
> + #define CACHE_LINE 32
> + #define BLOCK_CYCLES 30
> +#else
> + #define CACHE_LINE 32
> + #define BLOCK_CYCLES 11
> +#endif
> +
> +/* Pre-fetch look ahead = ceil (latency / block-cycles) */
> +#define PREF_AHEAD (LATENCY_CYCLES / BLOCK_CYCLES \
> + + ((LATENCY_CYCLES % BLOCK_CYCLES) == 0 ? 0 : 1))
> +
> +/* Unroll-factor, controls how many words at a time in the core loop. */
> +#define BLOCK (CACHE_LINE == 128 ? 16 : 8)
> +
> +#define __overloadable
> +#if !defined(UNALIGNED_INSTR_SUPPORT)
> +/* does target have unaligned lw/ld/ualw/uald instructions? */
> + #define UNALIGNED_INSTR_SUPPORT 0
> +#if (__mips_isa_rev < 6 && !defined(__mips1))
> + #undef UNALIGNED_INSTR_SUPPORT
> + #define UNALIGNED_INSTR_SUPPORT 1
> + #endif
> +#endif
> +#if !defined(HW_UNALIGNED_SUPPORT)
> +/* Does target have hardware support for unaligned accesses? */
> + #define HW_UNALIGNED_SUPPORT 0
> + #if __mips_isa_rev >= 6
> + #undef HW_UNALIGNED_SUPPORT
> + #define HW_UNALIGNED_SUPPORT 1
> + #endif
> +#endif
> +#define ENABLE_PREFETCH 1
> +#if ENABLE_PREFETCH
> + #define PREFETCH(addr) __builtin_prefetch (addr, 0, 0)
> +#else
> + #define PREFETCH(addr)
> +#endif
> +
> +#include <string.h>
> +
> +#ifdef __mips64
> +typedef unsigned long long reg_t;
> +typedef struct
> +{
> + reg_t B0:8, B1:8, B2:8, B3:8, B4:8, B5:8, B6:8, B7:8;
> +} bits_t;
> +#else
> +typedef unsigned long reg_t;
> +typedef struct
> +{
> + reg_t B0:8, B1:8, B2:8, B3:8;
> +} bits_t;
> +#endif
> +
> +#define CACHE_LINES_PER_BLOCK ((BLOCK * sizeof (reg_t) > CACHE_LINE) ? \
> + (BLOCK * sizeof (reg_t) / CACHE_LINE) \
> + : 1)
> +
> +typedef union
> +{
> + reg_t v;
> + bits_t b;
> +} bitfields_t;
> +
> +#define DO_BYTE(a, i) \
> + a[i] = bw.b.B##i; \
> + len--; \
> + if(!len) return ret; \
> +
> +/* This code is called when aligning a pointer, there are remaining bytes
> + after doing word compares, or architecture does not have some form
> + of unaligned support. */
> +static inline void * __attribute__ ((always_inline))
> +do_bytes (void *a, const void *b, unsigned long len, void *ret)
> +{
> + unsigned char *x = (unsigned char *) a;
> + unsigned char *y = (unsigned char *) b;
> + unsigned long i;
> + /* 'len' might be zero here, so preloading the first two values
> + before the loop may access unallocated memory. */
> + for (i = 0; i < len; i++)
> + {
> + *x = *y;
> + x++;
> + y++;
> + }
> + return ret;
> +}
> +
> +/* This code is called to copy only remaining bytes within word or doubleword */
> +static inline void * __attribute__ ((always_inline))
> +do_bytes_remaining (void *a, const void *b, unsigned long len, void *ret)
> +{
> + unsigned char *x = (unsigned char *) a;
> + bitfields_t bw;
> + if(len > 0)
> + {
> + bw.v = *(reg_t *)b;
> + DO_BYTE(x, 0);
> + DO_BYTE(x, 1);
> + DO_BYTE(x, 2);
> +#ifdef __mips64
> + DO_BYTE(x, 3);
> + DO_BYTE(x, 4);
> + DO_BYTE(x, 5);
> + DO_BYTE(x, 6);
> +#endif
> + }
> + return ret;
> +}
> +
> +static inline void * __attribute__ ((always_inline))
> +do_words_remaining (reg_t *a, const reg_t *b, unsigned long words,
> + unsigned long bytes, void *ret)
> +{
> + /* Use a set-back so that load/stores have incremented addresses in
> + order to promote bonding. */
> + int off = (BLOCK - words);
> + a -= off;
> + b -= off;
> + switch (off)
> + {
> + case 1: a[1] = b[1]; // Fall through
> + case 2: a[2] = b[2]; // Fall through
> + case 3: a[3] = b[3]; // Fall through
> + case 4: a[4] = b[4]; // Fall through
> + case 5: a[5] = b[5]; // Fall through
> + case 6: a[6] = b[6]; // Fall through
> + case 7: a[7] = b[7]; // Fall through
> +#if BLOCK==16
> + case 8: a[8] = b[8]; // Fall through
> + case 9: a[9] = b[9]; // Fall through
> + case 10: a[10] = b[10]; // Fall through
> + case 11: a[11] = b[11]; // Fall through
> + case 12: a[12] = b[12]; // Fall through
> + case 13: a[13] = b[13]; // Fall through
> + case 14: a[14] = b[14]; // Fall through
> + case 15: a[15] = b[15];
> +#endif
> + }
> + return do_bytes_remaining (a + BLOCK, b + BLOCK, bytes, ret);
> +}
> +
> +#if !HW_UNALIGNED_SUPPORT
> +#if UNALIGNED_INSTR_SUPPORT
> +/* For MIPS GCC, there are no unaligned builtins - so this struct forces
> + the compiler to treat the pointer access as unaligned. */
> +struct ulw
> +{
> + reg_t uli;
> +} __attribute__ ((packed));
> +static inline void * __attribute__ ((always_inline))
> +do_uwords_remaining (struct ulw *a, const reg_t *b, unsigned long words,
> + unsigned long bytes, void *ret)
> +{
> + /* Use a set-back so that load/stores have incremented addresses in
> + order to promote bonding. */
> + int off = (BLOCK - words);
> + a -= off;
> + b -= off;
> + switch (off)
> + {
> + case 1: a[1].uli = b[1]; // Fall through
> + case 2: a[2].uli = b[2]; // Fall through
> + case 3: a[3].uli = b[3]; // Fall through
> + case 4: a[4].uli = b[4]; // Fall through
> + case 5: a[5].uli = b[5]; // Fall through
> + case 6: a[6].uli = b[6]; // Fall through
> + case 7: a[7].uli = b[7]; // Fall through
> +#if BLOCK==16
> + case 8: a[8].uli = b[8]; // Fall through
> + case 9: a[9].uli = b[9]; // Fall through
> + case 10: a[10].uli = b[10]; // Fall through
> + case 11: a[11].uli = b[11]; // Fall through
> + case 12: a[12].uli = b[12]; // Fall through
> + case 13: a[13].uli = b[13]; // Fall through
> + case 14: a[14].uli = b[14]; // Fall through
> + case 15: a[15].uli = b[15];
> +#endif
> + }
> + return do_bytes_remaining (a + BLOCK, b + BLOCK, bytes, ret);
> +}
> +
> +/* The first pointer is not aligned while second pointer is. */
> +static void *
> +unaligned_words (struct ulw *a, const reg_t * b,
> + unsigned long words, unsigned long bytes, void *ret)
> +{
> + unsigned long i, words_by_block, words_by_1;
> + words_by_1 = words % BLOCK;
> + words_by_block = words / BLOCK;
> + for (; words_by_block > 0; words_by_block--)
> + {
> + if (words_by_block >= PREF_AHEAD - CACHE_LINES_PER_BLOCK)
> + for (i = 0; i < CACHE_LINES_PER_BLOCK; i++)
> + PREFETCH (b + (BLOCK / CACHE_LINES_PER_BLOCK) * (PREF_AHEAD + i));
> +
> + reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3];
> + reg_t y4 = b[4], y5 = b[5], y6 = b[6], y7 = b[7];
> + a[0].uli = y0;
> + a[1].uli = y1;
> + a[2].uli = y2;
> + a[3].uli = y3;
> + a[4].uli = y4;
> + a[5].uli = y5;
> + a[6].uli = y6;
> + a[7].uli = y7;
> +#if BLOCK==16
> + y0 = b[8], y1 = b[9], y2 = b[10], y3 = b[11];
> + y4 = b[12], y5 = b[13], y6 = b[14], y7 = b[15];
> + a[8].uli = y0;
> + a[9].uli = y1;
> + a[10].uli = y2;
> + a[11].uli = y3;
> + a[12].uli = y4;
> + a[13].uli = y5;
> + a[14].uli = y6;
> + a[15].uli = y7;
> +#endif
> + a += BLOCK;
> + b += BLOCK;
> + }
> +
> + /* Mop up any remaining bytes. */
> + return do_uwords_remaining (a, b, words_by_1, bytes, ret);
> +}
> +
> +#else
> +
> +/* No HW support or unaligned lw/ld/ualw/uald instructions. */
> +static void *
> +unaligned_words (reg_t * a, const reg_t * b,
> + unsigned long words, unsigned long bytes, void *ret)
> +{
> + unsigned long i;
> + unsigned char *x;
> + for (i = 0; i < words; i++)
> + {
> + bitfields_t bw;
> + bw.v = *((reg_t*) b);
> + x = (unsigned char *) a;
> + x[0] = bw.b.B0;
> + x[1] = bw.b.B1;
> + x[2] = bw.b.B2;
> + x[3] = bw.b.B3;
> +#ifdef __mips64
> + x[4] = bw.b.B4;
> + x[5] = bw.b.B5;
> + x[6] = bw.b.B6;
> + x[7] = bw.b.B7;
> +#endif
> + a += 1;
> + b += 1;
> + }
> + /* Mop up any remaining bytes. */
> + return do_bytes_remaining (a, b, bytes, ret);
> +}
> +
> +#endif /* UNALIGNED_INSTR_SUPPORT */
> +#endif /* HW_UNALIGNED_SUPPORT */
> +
> +/* both pointers are aligned, or first isn't and HW support for unaligned. */
> +static void *
> +aligned_words (reg_t * a, const reg_t * b,
> + unsigned long words, unsigned long bytes, void *ret)
> +{
> + unsigned long i, words_by_block, words_by_1;
> + words_by_1 = words % BLOCK;
> + words_by_block = words / BLOCK;
> + for (; words_by_block > 0; words_by_block--)
> + {
> + if(words_by_block >= PREF_AHEAD - CACHE_LINES_PER_BLOCK)
> + for (i = 0; i < CACHE_LINES_PER_BLOCK; i++)
> + PREFETCH (b + ((BLOCK / CACHE_LINES_PER_BLOCK) * (PREF_AHEAD + i)));
> +
> + reg_t x0 = b[0], x1 = b[1], x2 = b[2], x3 = b[3];
> + reg_t x4 = b[4], x5 = b[5], x6 = b[6], x7 = b[7];
> + a[0] = x0;
> + a[1] = x1;
> + a[2] = x2;
> + a[3] = x3;
> + a[4] = x4;
> + a[5] = x5;
> + a[6] = x6;
> + a[7] = x7;
> +#if BLOCK==16
> + x0 = b[8], x1 = b[9], x2 = b[10], x3 = b[11];
> + x4 = b[12], x5 = b[13], x6 = b[14], x7 = b[15];
> + a[8] = x0;
> + a[9] = x1;
> + a[10] = x2;
> + a[11] = x3;
> + a[12] = x4;
> + a[13] = x5;
> + a[14] = x6;
> + a[15] = x7;
> +#endif
> + a += BLOCK;
> + b += BLOCK;
> + }
> +
> + /* mop up any remaining bytes. */
> + return do_words_remaining (a, b, words_by_1, bytes, ret);
> +}
> +
> +void *
> +memcpy (void *a, const void *b, size_t len) __overloadable
> +{
> + unsigned long bytes, words, i;
> + void *ret = a;
> + /* shouldn't hit that often. */
> + if (len <= 8)
> + return do_bytes (a, b, len, a);
> +
> + /* Start pre-fetches ahead of time. */
> + if (len > CACHE_LINE * (PREF_AHEAD - 1))
> + for (i = 1; i < PREF_AHEAD - 1; i++)
> + PREFETCH ((char *)b + CACHE_LINE * i);
> + else
> + for (i = 1; i < len / CACHE_LINE; i++)
> + PREFETCH ((char *)b + CACHE_LINE * i);
> +
> + /* Align the second pointer to word/dword alignment.
> + Note that the pointer is only 32-bits for o32/n32 ABIs. For
> + n32, loads are done as 64-bit while address remains 32-bit. */
> + bytes = ((unsigned long) b) % (sizeof (reg_t));
> +
> + if (bytes)
> + {
> + bytes = (sizeof (reg_t)) - bytes;
> + if (bytes > len)
> + bytes = len;
> + do_bytes (a, b, bytes, ret);
> + if (len == bytes)
> + return ret;
> + len -= bytes;
> + a = (void *) (((unsigned char *) a) + bytes);
> + b = (const void *) (((unsigned char *) b) + bytes);
> + }
> +
> + /* Second pointer now aligned. */
> + words = len / sizeof (reg_t);
> + bytes = len % sizeof (reg_t);
> +
> +#if HW_UNALIGNED_SUPPORT
> + /* treat possible unaligned first pointer as aligned. */
> + return aligned_words (a, b, words, bytes, ret);
> +#else
> + if (((unsigned long) a) % sizeof (reg_t) == 0)
> + return aligned_words (a, b, words, bytes, ret);
> + /* need to use unaligned instructions on first pointer. */
> + return unaligned_words (a, b, words, bytes, ret);
> +#endif
> +}
> +
> +libc_hidden_builtin_def (memcpy)
> +
> +#else
> +#include <string/memcpy.c>
> +#endif
> diff --git a/sysdeps/mips/memset.S b/sysdeps/mips/memset.S
> deleted file mode 100644
> index 833381d73f..0000000000
> --- a/sysdeps/mips/memset.S
> +++ /dev/null
> @@ -1,430 +0,0 @@
> -/* Copyright (C) 2013-2025 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
> - <https://www.gnu.org/licenses/>. */
> -
> -#ifdef ANDROID_CHANGES
> -# include "machine/asm.h"
> -# include "machine/regdef.h"
> -# define PREFETCH_STORE_HINT PREFETCH_HINT_PREPAREFORSTORE
> -#elif _LIBC
> -# include <sysdep.h>
> -# include <regdef.h>
> -# include <sys/asm.h>
> -# define PREFETCH_STORE_HINT PREFETCH_HINT_PREPAREFORSTORE
> -#elif defined _COMPILING_NEWLIB
> -# include "machine/asm.h"
> -# include "machine/regdef.h"
> -# define PREFETCH_STORE_HINT PREFETCH_HINT_PREPAREFORSTORE
> -#else
> -# include <regdef.h>
> -# include <sys/asm.h>
> -#endif
> -
> -/* Check to see if the MIPS architecture we are compiling for supports
> - prefetching. */
> -
> -#if (__mips == 4) || (__mips == 5) || (__mips == 32) || (__mips == 64)
> -# ifndef DISABLE_PREFETCH
> -# define USE_PREFETCH
> -# endif
> -#endif
> -
> -#if defined(_MIPS_SIM) && ((_MIPS_SIM == _ABI64) || (_MIPS_SIM == _ABIN32))
> -# ifndef DISABLE_DOUBLE
> -# define USE_DOUBLE
> -# endif
> -#endif
> -
> -#ifndef USE_DOUBLE
> -# ifndef DISABLE_DOUBLE_ALIGN
> -# define DOUBLE_ALIGN
> -# endif
> -#endif
> -
> -
> -/* Some asm.h files do not have the L macro definition. */
> -#ifndef L
> -# if _MIPS_SIM == _ABIO32
> -# define L(label) $L ## label
> -# else
> -# define L(label) .L ## label
> -# endif
> -#endif
> -
> -/* Some asm.h files do not have the PTR_ADDIU macro definition. */
> -#ifndef PTR_ADDIU
> -# ifdef USE_DOUBLE
> -# define PTR_ADDIU daddiu
> -# else
> -# define PTR_ADDIU addiu
> -# endif
> -#endif
> -
> -/* New R6 instructions that may not be in asm.h. */
> -#ifndef PTR_LSA
> -# if _MIPS_SIM == _ABI64
> -# define PTR_LSA dlsa
> -# else
> -# define PTR_LSA lsa
> -# endif
> -#endif
> -
> -#if __mips_isa_rev > 5 && defined (__mips_micromips)
> -# define PTR_BC bc16
> -#else
> -# define PTR_BC bc
> -#endif
> -
> -/* Using PREFETCH_HINT_PREPAREFORSTORE instead of PREFETCH_STORE
> - or PREFETCH_STORE_STREAMED offers a large performance advantage
> - but PREPAREFORSTORE has some special restrictions to consider.
> -
> - Prefetch with the 'prepare for store' hint does not copy a memory
> - location into the cache, it just allocates a cache line and zeros
> - it out. This means that if you do not write to the entire cache
> - line before writing it out to memory some data will get zero'ed out
> - when the cache line is written back to memory and data will be lost.
> -
> - There are ifdef'ed sections of this memcpy to make sure that it does not
> - do prefetches on cache lines that are not going to be completely written.
> - This code is only needed and only used when PREFETCH_STORE_HINT is set to
> - PREFETCH_HINT_PREPAREFORSTORE. This code assumes that cache lines are
> - less than MAX_PREFETCH_SIZE bytes and if the cache line is larger it will
> - not work correctly. */
> -
> -#ifdef USE_PREFETCH
> -# define PREFETCH_HINT_STORE 1
> -# define PREFETCH_HINT_STORE_STREAMED 5
> -# define PREFETCH_HINT_STORE_RETAINED 7
> -# define PREFETCH_HINT_PREPAREFORSTORE 30
> -
> -/* If we have not picked out what hints to use at this point use the
> - standard load and store prefetch hints. */
> -# ifndef PREFETCH_STORE_HINT
> -# define PREFETCH_STORE_HINT PREFETCH_HINT_STORE
> -# endif
> -
> -/* We double everything when USE_DOUBLE is true so we do 2 prefetches to
> - get 64 bytes in that case. The assumption is that each individual
> - prefetch brings in 32 bytes. */
> -# ifdef USE_DOUBLE
> -# define PREFETCH_CHUNK 64
> -# define PREFETCH_FOR_STORE(chunk, reg) \
> - pref PREFETCH_STORE_HINT, (chunk)*64(reg); \
> - pref PREFETCH_STORE_HINT, ((chunk)*64)+32(reg)
> -# else
> -# define PREFETCH_CHUNK 32
> -# define PREFETCH_FOR_STORE(chunk, reg) \
> - pref PREFETCH_STORE_HINT, (chunk)*32(reg)
> -# endif
> -
> -/* MAX_PREFETCH_SIZE is the maximum size of a prefetch, it must not be less
> - than PREFETCH_CHUNK, the assumed size of each prefetch. If the real size
> - of a prefetch is greater than MAX_PREFETCH_SIZE and the PREPAREFORSTORE
> - hint is used, the code will not work correctly. If PREPAREFORSTORE is not
> - used than MAX_PREFETCH_SIZE does not matter. */
> -# define MAX_PREFETCH_SIZE 128
> -/* PREFETCH_LIMIT is set based on the fact that we never use an offset greater
> - than 5 on a STORE prefetch and that a single prefetch can never be larger
> - than MAX_PREFETCH_SIZE. We add the extra 32 when USE_DOUBLE is set because
> - we actually do two prefetches in that case, one 32 bytes after the other. */
> -# ifdef USE_DOUBLE
> -# define PREFETCH_LIMIT (5 * PREFETCH_CHUNK) + 32 + MAX_PREFETCH_SIZE
> -# else
> -# define PREFETCH_LIMIT (5 * PREFETCH_CHUNK) + MAX_PREFETCH_SIZE
> -# endif
> -
> -# if (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE) \
> - && ((PREFETCH_CHUNK * 4) < MAX_PREFETCH_SIZE)
> -/* We cannot handle this because the initial prefetches may fetch bytes that
> - are before the buffer being copied. We start copies with an offset
> - of 4 so avoid this situation when using PREPAREFORSTORE. */
> -# error "PREFETCH_CHUNK is too large and/or MAX_PREFETCH_SIZE is too small."
> -# endif
> -#else /* USE_PREFETCH not defined */
> -# define PREFETCH_FOR_STORE(offset, reg)
> -#endif
> -
> -#if __mips_isa_rev > 5
> -# if (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
> -# undef PREFETCH_STORE_HINT
> -# define PREFETCH_STORE_HINT PREFETCH_HINT_STORE_STREAMED
> -# endif
> -# define R6_CODE
> -#endif
> -
> -/* Allow the routine to be named something else if desired. */
> -#ifndef MEMSET_NAME
> -# define MEMSET_NAME memset
> -#endif
> -
> -/* We load/store 64 bits at a time when USE_DOUBLE is true.
> - The C_ prefix stands for CHUNK and is used to avoid macro name
> - conflicts with system header files. */
> -
> -#ifdef USE_DOUBLE
> -# define C_ST sd
> -# ifdef __MIPSEB
> -# define C_STHI sdl /* high part is left in big-endian */
> -# else
> -# define C_STHI sdr /* high part is right in little-endian */
> -# endif
> -#else
> -# define C_ST sw
> -# ifdef __MIPSEB
> -# define C_STHI swl /* high part is left in big-endian */
> -# else
> -# define C_STHI swr /* high part is right in little-endian */
> -# endif
> -#endif
> -
> -/* Bookkeeping values for 32 vs. 64 bit mode. */
> -#ifdef USE_DOUBLE
> -# define NSIZE 8
> -# define NSIZEMASK 0x3f
> -# define NSIZEDMASK 0x7f
> -#else
> -# define NSIZE 4
> -# define NSIZEMASK 0x1f
> -# define NSIZEDMASK 0x3f
> -#endif
> -#define UNIT(unit) ((unit)*NSIZE)
> -#define UNITM1(unit) (((unit)*NSIZE)-1)
> -
> -#ifdef ANDROID_CHANGES
> -LEAF(MEMSET_NAME,0)
> -#else
> -LEAF(MEMSET_NAME)
> -#endif
> -
> - .set nomips16
> -/* If the size is less than 4*NSIZE (16 or 32), go to L(lastb). Regardless of
> - size, copy dst pointer to v0 for the return value. */
> - slti t2,a2,(4 * NSIZE)
> - move v0,a0
> - bne t2,zero,L(lastb)
> -
> -/* If memset value is not zero, we copy it to all the bytes in a 32 or 64
> - bit word. */
> - PTR_SUBU a3,zero,a0
> - beq a1,zero,L(set0) /* If memset value is zero no smear */
> - nop
> -
> - /* smear byte into 32 or 64 bit word */
> -#if ((__mips == 64) || (__mips == 32)) && (__mips_isa_rev >= 2)
> -# ifdef USE_DOUBLE
> - dins a1, a1, 8, 8 /* Replicate fill byte into half-word. */
> - dins a1, a1, 16, 16 /* Replicate fill byte into word. */
> - dins a1, a1, 32, 32 /* Replicate fill byte into dbl word. */
> -# else
> - ins a1, a1, 8, 8 /* Replicate fill byte into half-word. */
> - ins a1, a1, 16, 16 /* Replicate fill byte into word. */
> -# endif
> -#else
> -# ifdef USE_DOUBLE
> - and a1,0xff
> - dsll t2,a1,8
> - or a1,t2
> - dsll t2,a1,16
> - or a1,t2
> - dsll t2,a1,32
> - or a1,t2
> -# else
> - and a1,0xff
> - sll t2,a1,8
> - or a1,t2
> - sll t2,a1,16
> - or a1,t2
> -# endif
> -#endif
> -
> -/* If the destination address is not aligned do a partial store to get it
> - aligned. If it is already aligned just jump to L(aligned). */
> -L(set0):
> -#ifndef R6_CODE
> - andi t2,a3,(NSIZE-1) /* word-unaligned address? */
> - PTR_SUBU a2,a2,t2
> - beq t2,zero,L(aligned) /* t2 is the unalignment count */
> - C_STHI a1,0(a0)
> - PTR_ADDU a0,a0,t2
> -#else /* R6_CODE */
> - andi t2,a0,7
> -# ifdef __mips_micromips
> - auipc t9,%pcrel_hi(L(atable))
> - addiu t9,t9,%pcrel_lo(L(atable)+4)
> - PTR_LSA t9,t2,t9,1
> -# else
> - lapc t9,L(atable)
> - PTR_LSA t9,t2,t9,2
> -# endif
> - jrc t9
> -L(atable):
> - PTR_BC L(aligned)
> - PTR_BC L(lb7)
> - PTR_BC L(lb6)
> - PTR_BC L(lb5)
> - PTR_BC L(lb4)
> - PTR_BC L(lb3)
> - PTR_BC L(lb2)
> - PTR_BC L(lb1)
> -L(lb7):
> - sb a1,6(a0)
> -L(lb6):
> - sb a1,5(a0)
> -L(lb5):
> - sb a1,4(a0)
> -L(lb4):
> - sb a1,3(a0)
> -L(lb3):
> - sb a1,2(a0)
> -L(lb2):
> - sb a1,1(a0)
> -L(lb1):
> - sb a1,0(a0)
> -
> - li t9,NSIZE
> - subu t2,t9,t2
> - PTR_SUBU a2,a2,t2
> - PTR_ADDU a0,a0,t2
> -#endif /* R6_CODE */
> -
> -L(aligned):
> -/* If USE_DOUBLE is not set we may still want to align the data on a 16
> - byte boundary instead of an 8 byte boundary to maximize the opportunity
> - of proAptiv chips to do memory bonding (combining two sequential 4
> - byte stores into one 8 byte store). We know there are at least 4 bytes
> - left to store or we would have jumped to L(lastb) earlier in the code. */
> -#ifdef DOUBLE_ALIGN
> - andi t2,a3,4
> - PTR_SUBU a2,a2,t2
> - beq t2,zero,L(double_aligned)
> - sw a1,0(a0)
> - PTR_ADDU a0,a0,t2
> -L(double_aligned):
> -#endif
> -
> -/* Now the destination is aligned to (word or double word) aligned address
> - Set a2 to count how many bytes we have to copy after all the 64/128 byte
> - chunks are copied and a3 to the dest pointer after all the 64/128 byte
> - chunks have been copied. We will loop, incrementing a0 until it equals
> - a3. */
> - andi t8,a2,NSIZEDMASK /* any whole 64-byte/128-byte chunks? */
> - PTR_SUBU a3,a2,t8 /* subtract from a2 the reminder */
> - beq a2,t8,L(chkw) /* if a2==t8, no 64-byte/128-byte chunks */
> - PTR_ADDU a3,a0,a3 /* Now a3 is the final dst after loop */
> -
> -/* When in the loop we may prefetch with the 'prepare to store' hint,
> - in this case the a0+x should not be past the "t0-32" address. This
> - means: for x=128 the last "safe" a0 address is "t0-160". Alternatively,
> - for x=64 the last "safe" a0 address is "t0-96" In the current version we
> - will use "prefetch hint,128(a0)", so "t0-160" is the limit. */
> -#if defined(USE_PREFETCH) \
> - && (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
> - PTR_ADDU t0,a0,a2 /* t0 is the "past the end" address */
> - PTR_SUBU t9,t0,PREFETCH_LIMIT /* t9 is the "last safe pref" address */
> -#endif
> -#if defined(USE_PREFETCH) \
> - && (PREFETCH_STORE_HINT != PREFETCH_HINT_PREPAREFORSTORE)
> - PREFETCH_FOR_STORE (1, a0)
> - PREFETCH_FOR_STORE (2, a0)
> - PREFETCH_FOR_STORE (3, a0)
> -#endif
> -
> -L(loop16w):
> -#if defined(USE_PREFETCH) \
> - && (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
> - sltu v1,t9,a0 /* If a0 > t9 don't use next prefetch */
> - bgtz v1,L(skip_pref)
> -#endif
> -#ifdef R6_CODE
> - PREFETCH_FOR_STORE (2, a0)
> -#else
> - PREFETCH_FOR_STORE (4, a0)
> - PREFETCH_FOR_STORE (5, a0)
> -#endif
> -L(skip_pref):
> - C_ST a1,UNIT(0)(a0)
> - C_ST a1,UNIT(1)(a0)
> - C_ST a1,UNIT(2)(a0)
> - C_ST a1,UNIT(3)(a0)
> - C_ST a1,UNIT(4)(a0)
> - C_ST a1,UNIT(5)(a0)
> - C_ST a1,UNIT(6)(a0)
> - C_ST a1,UNIT(7)(a0)
> - C_ST a1,UNIT(8)(a0)
> - C_ST a1,UNIT(9)(a0)
> - C_ST a1,UNIT(10)(a0)
> - C_ST a1,UNIT(11)(a0)
> - C_ST a1,UNIT(12)(a0)
> - C_ST a1,UNIT(13)(a0)
> - C_ST a1,UNIT(14)(a0)
> - C_ST a1,UNIT(15)(a0)
> - PTR_ADDIU a0,a0,UNIT(16) /* adding 64/128 to dest */
> - bne a0,a3,L(loop16w)
> - move a2,t8
> -
> -/* Here we have dest word-aligned but less than 64-bytes or 128 bytes to go.
> - Check for a 32(64) byte chunk and copy if there is one. Otherwise
> - jump down to L(chk1w) to handle the tail end of the copy. */
> -L(chkw):
> - andi t8,a2,NSIZEMASK /* is there a 32-byte/64-byte chunk. */
> - /* the t8 is the reminder count past 32-bytes */
> - beq a2,t8,L(chk1w)/* when a2==t8, no 32-byte chunk */
> - C_ST a1,UNIT(0)(a0)
> - C_ST a1,UNIT(1)(a0)
> - C_ST a1,UNIT(2)(a0)
> - C_ST a1,UNIT(3)(a0)
> - C_ST a1,UNIT(4)(a0)
> - C_ST a1,UNIT(5)(a0)
> - C_ST a1,UNIT(6)(a0)
> - C_ST a1,UNIT(7)(a0)
> - PTR_ADDIU a0,a0,UNIT(8)
> -
> -/* Here we have less than 32(64) bytes to set. Set up for a loop to
> - copy one word (or double word) at a time. Set a2 to count how many
> - bytes we have to copy after all the word (or double word) chunks are
> - copied and a3 to the dest pointer after all the (d)word chunks have
> - been copied. We will loop, incrementing a0 until a0 equals a3. */
> -L(chk1w):
> - andi a2,t8,(NSIZE-1) /* a2 is the reminder past one (d)word chunks */
> - PTR_SUBU a3,t8,a2 /* a3 is count of bytes in one (d)word chunks */
> - beq a2,t8,L(lastb)
> - PTR_ADDU a3,a0,a3 /* a3 is the dst address after loop */
> -
> -/* copying in words (4-byte or 8 byte chunks) */
> -L(wordCopy_loop):
> - PTR_ADDIU a0,a0,UNIT(1)
> - C_ST a1,UNIT(-1)(a0)
> - bne a0,a3,L(wordCopy_loop)
> -
> -/* Copy the last 8 (or 16) bytes */
> -L(lastb):
> - PTR_ADDU a3,a0,a2 /* a3 is the last dst address */
> - blez a2,L(leave)
> -L(lastbloop):
> - PTR_ADDIU a0,a0,1
> - sb a1,-1(a0)
> - bne a0,a3,L(lastbloop)
> -L(leave):
> - jr ra
> -
> - .set at
> -END(MEMSET_NAME)
> -#ifndef ANDROID_CHANGES
> -# ifdef _LIBC
> -libc_hidden_builtin_def (MEMSET_NAME)
> -# endif
> -#endif
> diff --git a/sysdeps/mips/memset.c b/sysdeps/mips/memset.c
> new file mode 100644
> index 0000000000..6082a1754d
> --- /dev/null
> +++ b/sysdeps/mips/memset.c
> @@ -0,0 +1,188 @@
> +/*
> + * Copyright (C) 2024 MIPS Tech, LLC
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are met:
> + *
> + * 1. Redistributions of source code must retain the above copyright notice,
> + * this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright notice,
> + * this list of conditions and the following disclaimer in the documentation
> + * and/or other materials provided with the distribution.
> + * 3. Neither the name of the copyright holder nor the names of its
> + * contributors may be used to endorse or promote products derived from this
> + * software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> +*/
> +
> +#ifdef __GNUC__
> +
> +#undef memset
> +
> +#include <string.h>
> +
> +#if _MIPS_SIM == _ABIO32
> +#define SIZEOF_reg_t 4
> +typedef unsigned long reg_t;
> +#else
> +#define SIZEOF_reg_t 8
> +typedef unsigned long long reg_t;
> +#endif
> +
> +typedef struct bits8
> +{
> + reg_t B0:8, B1:8, B2:8, B3:8;
> +#if SIZEOF_reg_t == 8
> + reg_t B4:8, B5:8, B6:8, B7:8;
> +#endif
> +} bits8_t;
> +typedef struct bits16
> +{
> + reg_t B0:16, B1:16;
> +#if SIZEOF_reg_t == 8
> + reg_t B2:16, B3:16;
> +#endif
> +} bits16_t;
> +typedef struct bits32
> +{
> + reg_t B0:32;
> +#if SIZEOF_reg_t == 8
> + reg_t B1:32;
> +#endif
> +} bits32_t;
> +
> +/* This union assumes that small structures can be in registers. If
> + not, then memory accesses will be done - not optimal, but ok. */
> +typedef union
> +{
> + reg_t v;
> + bits8_t b8;
> + bits16_t b16;
> + bits32_t b32;
> +} bitfields_t;
> +
> +/* This code is called when aligning a pointer or there are remaining bytes
> + after doing word sets. */
> +static inline void * __attribute__ ((always_inline))
> +do_bytes (void *a, void *retval, unsigned char fill, const unsigned long len)
> +{
> + unsigned char *x = ((unsigned char *) a);
> + unsigned long i;
> +
> + for (i = 0; i < len; i++)
> + *x++ = fill;
> +
> + return retval;
> +}
> +
> +/* Pointer is aligned. */
> +static void *
> +do_aligned_words (reg_t * a, void * retval, reg_t fill,
> + unsigned long words, unsigned long bytes)
> +{
> + unsigned long i, words_by_1, words_by_16;
> +
> + words_by_1 = words % 16;
> + words_by_16 = words / 16;
> +
> + /*
> + * Note: prefetching the store memory is not beneficial on most
> + * cores since the ls/st unit has store buffers that will be filled
> + * before the cache line is actually needed.
> + *
> + * Also, using prepare-for-store cache op is problematic since we
> + * don't know the implementation-defined cache line length and we
> + * don't want to touch unintended memory.
> + */
> + for (i = 0; i < words_by_16; i++)
> + {
> + a[0] = fill;
> + a[1] = fill;
> + a[2] = fill;
> + a[3] = fill;
> + a[4] = fill;
> + a[5] = fill;
> + a[6] = fill;
> + a[7] = fill;
> + a[8] = fill;
> + a[9] = fill;
> + a[10] = fill;
> + a[11] = fill;
> + a[12] = fill;
> + a[13] = fill;
> + a[14] = fill;
> + a[15] = fill;
> + a += 16;
> + }
> +
> + /* do remaining words. */
> + for (i = 0; i < words_by_1; i++)
> + *a++ = fill;
> +
> + /* mop up any remaining bytes. */
> + return do_bytes (a, retval, fill, bytes);
> +}
> +
> +void *
> +inhibit_loop_to_libcall
> +memset (void *a, int ifill, size_t len)
> +{
> + unsigned long bytes, words;
> + bitfields_t fill;
> + void *retval = (void *) a;
> +
> + /* shouldn't hit that often. */
> + if (len < 16)
> + return do_bytes (a, retval, ifill, len);
> +
> + /* Align the pointer to word/dword alignment.
> + Note that the pointer is only 32-bits for o32/n32 ABIs. For
> + n32, loads are done as 64-bit while address remains 32-bit. */
> + bytes = ((unsigned long) a) % (sizeof (reg_t) * 2);
> + if (bytes)
> + {
> + bytes = (sizeof (reg_t) * 2 - bytes);
> + if (bytes > len)
> + bytes = len;
> + do_bytes (a, retval, ifill, bytes);
> + if (len == bytes)
> + return retval;
> + len -= bytes;
> + a = (void *) (((unsigned char *) a) + bytes);
> + }
> +
> + /* Create correct fill value for reg_t sized variable. */
> + if (ifill != 0)
> + {
> + fill.b8.B0 = (unsigned char) ifill;
> + fill.b8.B1 = fill.b8.B0;
> + fill.b16.B1 = fill.b16.B0;
> +#if SIZEOF_reg_t == 8
> + fill.b32.B1 = fill.b32.B0;
> +#endif
> + }
> + else
> + fill.v = 0;
> +
> + words = len / sizeof (reg_t);
> + bytes = len % sizeof (reg_t);
> + return do_aligned_words (a, retval, fill.v, words, bytes);
> +}
> +
> +
> +libc_hidden_builtin_def (memset)
> +
> +#else
> +#include <string/memset.c>
> +#endif
More information about the Libc-alpha
mailing list