// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build mips || mipsle

#include "textflag.h"

#ifdef GOARCH_mips
#define MOVWHI  MOVWL
#define MOVWLO  MOVWR
#else
#define MOVWHI  MOVWR
#define MOVWLO  MOVWL
#endif

// See memmove Go doc for important implementation constraints.

// func memmove(to, from unsafe.Pointer, n uintptr)
TEXT runtime·memmove(SB),NOSPLIT,$-0-12
	MOVW	n+8(FP), R3
	MOVW	from+4(FP), R2
	MOVW	to+0(FP), R1

	ADDU	R3, R2, R4	// end pointer for source
	ADDU	R3, R1, R5	// end pointer for destination

	// if destination is ahead of source, start at the end of the buffer and go backward.
	SGTU	R1, R2, R6
	BNE	R6, backward

	// if less than 4 bytes, use byte by byte copying
	SGTU	$4, R3, R6
	BNE	R6, f_small_copy

	// align destination to 4 bytes
	AND	$3, R1, R6
	BEQ	R6, f_dest_aligned
	SUBU	R1, R0, R6
	AND	$3, R6
	MOVWHI	0(R2), R7
	SUBU	R6, R3
	MOVWLO	3(R2), R7
	ADDU	R6, R2
	MOVWHI	R7, 0(R1)
	ADDU	R6, R1

f_dest_aligned:
	AND	$31, R3, R7
	AND	$3, R3, R6
	SUBU	R7, R5, R7	// end pointer for 32-byte chunks
	SUBU	R6, R5, R6	// end pointer for 4-byte chunks

	// if source is not aligned, use unaligned reads
	AND	$3, R2, R8
	BNE	R8, f_large_ua

f_large:
	BEQ	R1, R7, f_words
	ADDU	$32, R1
	MOVW	0(R2), R8
	MOVW	4(R2), R9
	MOVW	8(R2), R10
	MOVW	12(R2), R11
	MOVW	16(R2), R12
	MOVW	20(R2), R13
	MOVW	24(R2), R14
	MOVW	28(R2), R15
	ADDU	$32, R2
	MOVW	R8, -32(R1)
	MOVW	R9, -28(R1)
	MOVW	R10, -24(R1)
	MOVW	R11, -20(R1)
	MOVW	R12, -16(R1)
	MOVW	R13, -12(R1)
	MOVW	R14, -8(R1)
	MOVW	R15, -4(R1)
	JMP	f_large

f_words:
	BEQ	R1, R6, f_tail
	ADDU	$4, R1
	MOVW	0(R2), R8
	ADDU	$4, R2
	MOVW	R8, -4(R1)
	JMP	f_words

f_tail:
	BEQ	R1, R5, ret
	MOVWLO	-1(R4), R8
	MOVWLO	R8, -1(R5)

ret:
	RET

f_large_ua:
	BEQ	R1, R7, f_words_ua
	ADDU	$32, R1
	MOVWHI	0(R2), R8
	MOVWHI	4(R2), R9
	MOVWHI	8(R2), R10
	MOVWHI	12(R2), R11
	MOVWHI	16(R2), R12
	MOVWHI	20(R2), R13
	MOVWHI	24(R2), R14
	MOVWHI	28(R2), R15
	MOVWLO	3(R2), R8
	MOVWLO	7(R2), R9
	MOVWLO	11(R2), R10
	MOVWLO	15(R2), R11
	MOVWLO	19(R2), R12
	MOVWLO	23(R2), R13
	MOVWLO	27(R2), R14
	MOVWLO	31(R2), R15
	ADDU	$32, R2
	MOVW	R8, -32(R1)
	MOVW	R9, -28(R1)
	MOVW	R10, -24(R1)
	MOVW	R11, -20(R1)
	MOVW	R12, -16(R1)
	MOVW	R13, -12(R1)
	MOVW	R14, -8(R1)
	MOVW	R15, -4(R1)
	JMP	f_large_ua

f_words_ua:
	BEQ	R1, R6, f_tail_ua
	MOVWHI	0(R2), R8
	ADDU	$4, R1
	MOVWLO	3(R2), R8
	ADDU	$4, R2
	MOVW	R8, -4(R1)
	JMP	f_words_ua

f_tail_ua:
	BEQ	R1, R5, ret
	MOVWHI	-4(R4), R8
	MOVWLO	-1(R4), R8
	MOVWLO	R8, -1(R5)
	JMP	ret

f_small_copy:
	BEQ	R1, R5, ret
	ADDU	$1, R1
	MOVB	0(R2), R6
	ADDU	$1, R2
	MOVB	R6, -1(R1)
	JMP	f_small_copy

backward:
	SGTU	$4, R3, R6
	BNE	R6, b_small_copy

	AND	$3, R5, R6
	BEQ	R6, b_dest_aligned
	MOVWHI	-4(R4), R7
	SUBU	R6, R3
	MOVWLO	-1(R4), R7
	SUBU	R6, R4
	MOVWLO	R7, -1(R5)
	SUBU	R6, R5

b_dest_aligned:
	AND	$31, R3, R7
	AND	$3, R3, R6
	ADDU	R7, R1, R7
	ADDU	R6, R1, R6

	AND	$3, R4, R8
	BNE	R8, b_large_ua

b_large:
	BEQ	R5, R7, b_words
	ADDU	$-32, R5
	MOVW	-4(R4), R8
	MOVW	-8(R4), R9
	MOVW	-12(R4), R10
	MOVW	-16(R4), R11
	MOVW	-20(R4), R12
	MOVW	-24(R4), R13
	MOVW	-28(R4), R14
	MOVW	-32(R4), R15
	ADDU	$-32, R4
	MOVW	R8, 28(R5)
	MOVW	R9, 24(R5)
	MOVW	R10, 20(R5)
	MOVW	R11, 16(R5)
	MOVW	R12, 12(R5)
	MOVW	R13, 8(R5)
	MOVW	R14, 4(R5)
	MOVW	R15, 0(R5)
	JMP	b_large

b_words:
	BEQ	R5, R6, b_tail
	ADDU	$-4, R5
	MOVW	-4(R4), R8
	ADDU	$-4, R4
	MOVW	R8, 0(R5)
	JMP	b_words

b_tail:
	BEQ	R5, R1, ret
	MOVWHI	0(R2), R8	// R2 and R1 have the same alignment so we don't need to load a whole word
	MOVWHI	R8, 0(R1)
	JMP	ret

b_large_ua:
	BEQ	R5, R7, b_words_ua
	ADDU	$-32, R5
	MOVWHI	-4(R4), R8
	MOVWHI	-8(R4), R9
	MOVWHI	-12(R4), R10
	MOVWHI	-16(R4), R11
	MOVWHI	-20(R4), R12
	MOVWHI	-24(R4), R13
	MOVWHI	-28(R4), R14
	MOVWHI	-32(R4), R15
	MOVWLO	-1(R4), R8
	MOVWLO	-5(R4), R9
	MOVWLO	-9(R4), R10
	MOVWLO	-13(R4), R11
	MOVWLO	-17(R4), R12
	MOVWLO	-21(R4), R13
	MOVWLO	-25(R4), R14
	MOVWLO	-29(R4), R15
	ADDU	$-32, R4
	MOVW	R8, 28(R5)
	MOVW	R9, 24(R5)
	MOVW	R10, 20(R5)
	MOVW	R11, 16(R5)
	MOVW	R12, 12(R5)
	MOVW	R13, 8(R5)
	MOVW	R14, 4(R5)
	MOVW	R15, 0(R5)
	JMP	b_large_ua

b_words_ua:
	BEQ	R5, R6, b_tail_ua
	MOVWHI	-4(R4), R8
	ADDU	$-4, R5
	MOVWLO	-1(R4), R8
	ADDU	$-4, R4
	MOVW	R8, 0(R5)
	JMP	b_words_ua

b_tail_ua:
	BEQ	R5, R1, ret
	MOVWHI	(R2), R8
	MOVWLO	3(R2), R8
	MOVWHI	R8, 0(R1)
	JMP ret

b_small_copy:
	BEQ	R5, R1, ret
	ADDU	$-1, R5
	MOVB	-1(R4), R6
	ADDU	$-1, R4
	MOVB	R6, 0(R5)
	JMP	b_small_copy
