/*
 * Copyright (c) 2009
 *      MIPS Technologies, Inc., California.
 *
 * 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 MIPS Technologies, Inc., 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 MIPS TECHNOLOGIES, INC. ``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 MIPS TECHNOLOGIES, INC. 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.
 */

/************************************************************************
 *
 *  memcpy.S
 *  Version: "043009"
 *
 ************************************************************************/


/************************************************************************
 *  Include files
 ************************************************************************/

#include "machine/asm.h"


/* 
 * This routine could be optimized for MIPS64. The current code only
 * uses MIPS32 instructions.
 */	
#if defined(__MIPSEB__)
#  define LWHI	lwl		/* high part is left in big-endian	*/
#  define SWHI	swl		/* high part is left in big-endian	*/
#  define LWLO	lwr		/* low part is right in big-endian	*/
#  define SWLO	swr		/* low part is right in big-endian	*/
#endif

#if defined(__MIPSEL__)
#  define LWHI	lwr		/* high part is right in little-endian	*/
#  define SWHI	swr		/* high part is right in little-endian	*/
#  define LWLO	lwl		/* low part is left in big-endian	*/
#  define SWLO	swl		/* low part is left in big-endian	*/
#endif

LEAF(memcpy,0)

	.set	noreorder
	.set	noat
/*
 * 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.
 */
	subu	t0,a0,a1
	sra	AT,t0,31
	xor	t1,t0,AT
	subu	t0,t1,AT
	sltu	AT,t0,a2
	beq	AT,zero,.Lmemcpy
	 la	t9,memmove
	jr	t9
	 nop
.Lmemcpy:
	slti	AT,a2,8
	bne	AT,zero,.Llast8
	 move	v0,a0	# memcpy returns the dst pointer

# Test if the src and dst are word-aligned, or can be made word-aligned
	xor	t8,a1,a0
	andi	t8,t8,0x3		# t8 is a0/a1 word-displacement

	bne	t8,zero,.Lunaligned
	 negu	a3,a0

	andi	a3,a3,0x3	# we need to copy a3 bytes to make a0/a1 aligned
	beq	a3,zero,.Lchk16w # when a3=0 then the dst (a0) is word-aligned
	 subu	a2,a2,a3	# now a2 is the remining bytes count

	LWHI	t8,0(a1)
	addu	a1,a1,a3
	SWHI	t8,0(a0)
	addu	a0,a0,a3

# Now the dst/src are mutually word-aligned with word-aligned addresses
.Lchk16w:
	andi	t8,a2,0x3f	# any whole 64-byte chunks?
				# t8 is the byte count after 64-byte chunks

	beq	a2,t8,.Lchk8w	# if a2==t8, no 64-byte chunks
				# There will be at most 1 32-byte chunk after it
	 subu	a3,a2,t8	# subtract from a2 the reminder
                                # Here a3 counts bytes in 16w chunks
	addu	a3,a0,a3	# Now a3 is the final dst after 64-byte chunks

	addu	t0,a0,a2	# t0 is the "past the end" address

# When in the loop we exercise "pref 30,x(a0)", 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 "pref 30,128(a0)", so "t0-160" is the limit
	subu	t9,t0,160	# t9 is the "last safe pref 30,128(a0)" address

	pref    0,0(a1)		# bring the first line of src, addr 0
	pref    0,32(a1)	# bring the second line of src, addr 32
	pref    0,64(a1)	# bring the third line of src, addr 64
	pref	30,32(a0)	# safe, as we have at least 64 bytes ahead
# In case the a0 > t9 don't use "pref 30" at all
	sgtu	v1,a0,t9
	bgtz	v1,.Lloop16w	# skip "pref 30,64(a0)" for too short arrays
	 nop
# otherwise, start with using pref30
	pref	30,64(a0)
.Lloop16w:
	pref	0,96(a1)
	lw	t0,0(a1)
	bgtz	v1,.Lskip_pref30_96	# skip "pref 30,96(a0)"
	 lw	t1,4(a1)
	pref    30,96(a0)   # continue setting up the dest, addr 96
.Lskip_pref30_96:
	lw	t2,8(a1)
	lw	t3,12(a1)
	lw	t4,16(a1)
	lw	t5,20(a1)
	lw	t6,24(a1)
	lw	t7,28(a1)
        pref    0,128(a1)    # bring the next lines of src, addr 128

	sw	t0,0(a0)
	sw	t1,4(a0)
	sw	t2,8(a0)
	sw	t3,12(a0)
	sw	t4,16(a0)
	sw	t5,20(a0)
	sw	t6,24(a0)
	sw	t7,28(a0)

	lw	t0,32(a1)
	bgtz	v1,.Lskip_pref30_128	# skip "pref 30,128(a0)"
	 lw	t1,36(a1)
	pref    30,128(a0)   # continue setting up the dest, addr 128
.Lskip_pref30_128:
	lw	t2,40(a1)
	lw	t3,44(a1)
	lw	t4,48(a1)
	lw	t5,52(a1)
	lw	t6,56(a1)
	lw	t7,60(a1)
        pref    0, 160(a1)    # bring the next lines of src, addr 160

	sw	t0,32(a0)
	sw	t1,36(a0)
	sw	t2,40(a0)
	sw	t3,44(a0)
	sw	t4,48(a0)
	sw	t5,52(a0)
	sw	t6,56(a0)
	sw	t7,60(a0)

	addiu	a0,a0,64	# adding 64 to dest
	sgtu	v1,a0,t9
	bne	a0,a3,.Lloop16w
	 addiu	a1,a1,64	# adding 64 to src
	move	a2,t8

# Here we have src and dest word-aligned but less than 64-bytes to go

.Lchk8w:
	pref 0, 0x0(a1)
	andi	t8,a2,0x1f	# is there a 32-byte chunk?
				# the t8 is the reminder count past 32-bytes
	beq	a2,t8,.Lchk1w	# when a2=t8, no 32-byte chunk
	 nop

	lw	t0,0(a1)
	lw	t1,4(a1)
	lw	t2,8(a1)
	lw	t3,12(a1)
	lw	t4,16(a1)
	lw	t5,20(a1)
	lw	t6,24(a1)
	lw	t7,28(a1)
	addiu	a1,a1,32

	sw	t0,0(a0)
	sw	t1,4(a0)
	sw	t2,8(a0)
	sw	t3,12(a0)
	sw	t4,16(a0)
	sw	t5,20(a0)
	sw	t6,24(a0)
	sw	t7,28(a0)
	addiu	a0,a0,32

.Lchk1w:
	andi	a2,t8,0x3	# now a2 is the reminder past 1w chunks
	beq	a2,t8,.Llast8
	 subu	a3,t8,a2	# a3 is count of bytes in 1w chunks
	addu	a3,a0,a3	# now a3 is the dst address past the 1w chunks

# copying in words (4-byte chunks)
.LwordCopy_loop:
	lw	t3,0(a1)	# the first t3 may be equal t0 ... optimize?
	addiu	a1,a1,4
	addiu	a0,a0,4
	bne	a0,a3,.LwordCopy_loop
	 sw	t3,-4(a0)

# For the last (<8) bytes
.Llast8:
	blez	a2,.Lleave
	 addu	a3,a0,a2	# a3 is the last dst address
.Llast8loop:
	lb	v1,0(a1)
	addiu	a1,a1,1
	addiu	a0,a0,1
	bne	a0,a3,.Llast8loop
	 sb	v1,-1(a0)

.Lleave:
	j	ra
	 nop

#
# UNALIGNED case
#

.Lunaligned:
	# got here with a3="negu a0"
	andi	a3,a3,0x3	# test if the a0 is word aligned
	beqz	a3,.Lua_chk16w
	 subu	a2,a2,a3	# bytes left after initial a3 bytes

	LWHI	v1,0(a1)
	LWLO	v1,3(a1)
	addu	a1,a1,a3	# a3 may be here 1, 2 or 3
	SWHI	v1,0(a0)
	addu	a0,a0,a3	# below the dst will be word aligned (NOTE1)

.Lua_chk16w:
	andi	t8,a2,0x3f	# any whole 64-byte chunks?
				# t8 is the byte count after 64-byte chunks
	beq	a2,t8,.Lua_chk8w # if a2==t8, no 64-byte chunks
				# There will be at most 1 32-byte chunk after it
	 subu	a3,a2,t8	# subtract from a2 the reminder
                                # Here a3 counts bytes in 16w chunks
	addu	a3,a0,a3	# Now a3 is the final dst after 64-byte chunks

	addu	t0,a0,a2	# t0 is the "past the end" address

	subu	t9,t0,160	# t9 is the "last safe pref 30,128(a0)" address

	pref    0,0(a1)		# bring the first line of src, addr 0
	pref    0,32(a1)	# bring the second line of src, addr 32
	pref    0,64(a1)	# bring the third line of src, addr 64
	pref	30,32(a0)	# safe, as we have at least 64 bytes ahead
# In case the a0 > t9 don't use "pref 30" at all
	sgtu	v1,a0,t9
	bgtz	v1,.Lua_loop16w	# skip "pref 30,64(a0)" for too short arrays
	 nop
# otherwise, start with using pref30
	pref	30,64(a0)
.Lua_loop16w:
	pref	0,96(a1)
	LWHI	t0,0(a1)
	LWLO	t0,3(a1)
	LWHI	t1,4(a1)
	bgtz	v1,.Lua_skip_pref30_96
	 LWLO	t1,7(a1)
	pref    30,96(a0)   # continue setting up the dest, addr 96
.Lua_skip_pref30_96:
	LWHI	t2,8(a1)
	LWLO	t2,11(a1)
	LWHI	t3,12(a1)
	LWLO	t3,15(a1)
	LWHI	t4,16(a1)
	LWLO	t4,19(a1)
	LWHI	t5,20(a1)
	LWLO	t5,23(a1)
	LWHI	t6,24(a1)
	LWLO	t6,27(a1)
	LWHI	t7,28(a1)
	LWLO	t7,31(a1)
        pref    0,128(a1)    # bring the next lines of src, addr 128

	sw	t0,0(a0)
	sw	t1,4(a0)
	sw	t2,8(a0)
	sw	t3,12(a0)
	sw	t4,16(a0)
	sw	t5,20(a0)
	sw	t6,24(a0)
	sw	t7,28(a0)

	LWHI	t0,32(a1)
	LWLO	t0,35(a1)
	LWHI	t1,36(a1)
	bgtz	v1,.Lua_skip_pref30_128
	LWLO	t1,39(a1)
	pref    30,128(a0)   # continue setting up the dest, addr 128
.Lua_skip_pref30_128:
	LWHI	t2,40(a1)
	LWLO	t2,43(a1)
	LWHI	t3,44(a1)
	LWLO	t3,47(a1)
	LWHI	t4,48(a1)
	LWLO	t4,51(a1)
	LWHI	t5,52(a1)
	LWLO	t5,55(a1)
	LWHI	t6,56(a1)
	LWLO	t6,59(a1)
	LWHI	t7,60(a1)
	LWLO	t7,63(a1)
        pref    0, 160(a1)    # bring the next lines of src, addr 160

	sw	t0,32(a0)
	sw	t1,36(a0)
	sw	t2,40(a0)
	sw	t3,44(a0)
	sw	t4,48(a0)
	sw	t5,52(a0)
	sw	t6,56(a0)
	sw	t7,60(a0)

	addiu	a0,a0,64	# adding 64 to dest
	sgtu	v1,a0,t9
	bne	a0,a3,.Lua_loop16w
	 addiu	a1,a1,64	# adding 64 to src
	move	a2,t8

# Here we have src and dest word-aligned but less than 64-bytes to go

.Lua_chk8w:
	pref 0, 0x0(a1)
	andi	t8,a2,0x1f	# is there a 32-byte chunk?
				# the t8 is the reminder count
	beq	a2,t8,.Lua_chk1w # when a2=t8, no 32-byte chunk
	 nop

	LWHI	t0,0(a1)
	LWLO	t0,3(a1)
	LWHI	t1,4(a1)
	LWLO	t1,7(a1)
	LWHI	t2,8(a1)
	LWLO	t2,11(a1)
	LWHI	t3,12(a1)
	LWLO	t3,15(a1)
	LWHI	t4,16(a1)
	LWLO	t4,19(a1)
	LWHI	t5,20(a1)
	LWLO	t5,23(a1)
	LWHI	t6,24(a1)
	LWLO	t6,27(a1)
	LWHI	t7,28(a1)
	LWLO	t7,31(a1)
	addiu	a1,a1,32

	sw	t0,0(a0)
	sw	t1,4(a0)
	sw	t2,8(a0)
	sw	t3,12(a0)
	sw	t4,16(a0)
	sw	t5,20(a0)
	sw	t6,24(a0)
	sw	t7,28(a0)
	addiu	a0,a0,32

.Lua_chk1w:
	andi	a2,t8,0x3	# now a2 is the reminder past 1w chunks
	beq	a2,t8,.Lua_smallCopy
	 subu	a3,t8,a2	# a3 is count of bytes in 1w chunks
	addu	a3,a0,a3	# now a3 is the dst address past the 1w chunks

# copying in words (4-byte chunks)
.Lua_wordCopy_loop:
	LWHI	v1,0(a1)
	LWLO	v1,3(a1)
	addiu	a1,a1,4
	addiu	a0,a0,4		# note: dst=a0 is word aligned here, see NOTE1
	bne	a0,a3,.Lua_wordCopy_loop
	 sw	v1,-4(a0)

# Now less than 4 bytes (value in a2) left to copy
.Lua_smallCopy:
	beqz	a2,.Lleave
	addu	a3,a0,a2	# a3 is the last dst address
.Lua_smallCopy_loop:
	lb	v1,0(a1)
	addiu	a1,a1,1
	addiu	a0,a0,1
	bne	a0,a3,.Lua_smallCopy_loop
	 sb	v1,-1(a0)

	j	ra
	 nop

	.set	at
	.set	reorder

END(memcpy)


/************************************************************************
 *  Implementation : Static functions
 ************************************************************************/