// Inferno's libkern/memmove-arm.s
// http://code.google.com/p/inferno-os/source/browse/libkern/memmove-arm.s
//
//         Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
//         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
//         Portions Copyright 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "../../cmd/ld/textflag.h"

// TE or TS are spilled to the stack during bulk register moves.
TS = 0
TE = 8

// Warning: the linker will use R11 to synthesize certain instructions. Please
// take care and double check with objdump.
FROM = 11
N = 12
TMP = 12				/* N and TMP don't overlap */
TMP1 = 5

RSHIFT = 5
LSHIFT = 6
OFFSET = 7

BR0 = 0					/* shared with TS */
BW0 = 1
BR1 = 1
BW1 = 2
BR2 = 2
BW2 = 3
BR3 = 3
BW3 = 4

FW0 = 1
FR0 = 2
FW1 = 2
FR1 = 3
FW2 = 3
FR2 = 4
FW3 = 4
FR3 = 8					/* shared with TE */

TEXT runtime·memmove(SB), NOSPLIT, $4-12
_memmove:
	MOVW	to+0(FP), R(TS)
	MOVW	from+4(FP), R(FROM)
	MOVW	n+8(FP), R(N)

	ADD	R(N), R(TS), R(TE)	/* to end pointer */

	CMP	R(FROM), R(TS)
	BLS	_forward

_back:
	ADD	R(N), R(FROM)		/* from end pointer */
	CMP	$4, R(N)		/* need at least 4 bytes to copy */
	BLT	_b1tail

_b4align:				/* align destination on 4 */
	AND.S	$3, R(TE), R(TMP)
	BEQ	_b4aligned

	MOVBU.W	-1(R(FROM)), R(TMP)	/* pre-indexed */
	MOVBU.W	R(TMP), -1(R(TE))	/* pre-indexed */
	B	_b4align

_b4aligned:				/* is source now aligned? */
	AND.S	$3, R(FROM), R(TMP)
	BNE	_bunaligned

	ADD	$31, R(TS), R(TMP)	/* do 32-byte chunks if possible */
	MOVW	R(TS), savedts-4(SP)
_b32loop:
	CMP	R(TMP), R(TE)
	BLS	_b4tail

	MOVM.DB.W (R(FROM)), [R0-R7]
	MOVM.DB.W [R0-R7], (R(TE))
	B	_b32loop

_b4tail:				/* do remaining words if possible */
	MOVW	savedts-4(SP), R(TS)
	ADD	$3, R(TS), R(TMP)
_b4loop:
	CMP	R(TMP), R(TE)
	BLS	_b1tail

	MOVW.W	-4(R(FROM)), R(TMP1)	/* pre-indexed */
	MOVW.W	R(TMP1), -4(R(TE))	/* pre-indexed */
	B	_b4loop

_b1tail:				/* remaining bytes */
	CMP	R(TE), R(TS)
	BEQ	_return

	MOVBU.W	-1(R(FROM)), R(TMP)	/* pre-indexed */
	MOVBU.W	R(TMP), -1(R(TE))	/* pre-indexed */
	B	_b1tail

_forward:
	CMP	$4, R(N)		/* need at least 4 bytes to copy */
	BLT	_f1tail

_f4align:				/* align destination on 4 */
	AND.S	$3, R(TS), R(TMP)
	BEQ	_f4aligned

	MOVBU.P	1(R(FROM)), R(TMP)	/* implicit write back */
	MOVBU.P	R(TMP), 1(R(TS))	/* implicit write back */
	B	_f4align

_f4aligned:				/* is source now aligned? */
	AND.S	$3, R(FROM), R(TMP)
	BNE	_funaligned

	SUB	$31, R(TE), R(TMP)	/* do 32-byte chunks if possible */
	MOVW	R(TE), savedte-4(SP)
_f32loop:
	CMP	R(TMP), R(TS)
	BHS	_f4tail

	MOVM.IA.W (R(FROM)), [R1-R8] 
	MOVM.IA.W [R1-R8], (R(TS))
	B	_f32loop

_f4tail:
	MOVW	savedte-4(SP), R(TE)
	SUB	$3, R(TE), R(TMP)	/* do remaining words if possible */
_f4loop:
	CMP	R(TMP), R(TS)
	BHS	_f1tail

	MOVW.P	4(R(FROM)), R(TMP1)	/* implicit write back */
	MOVW.P	R(TMP1), 4(R(TS))	/* implicit write back */
	B	_f4loop

_f1tail:
	CMP	R(TS), R(TE)
	BEQ	_return

	MOVBU.P	1(R(FROM)), R(TMP)	/* implicit write back */
	MOVBU.P	R(TMP), 1(R(TS))	/* implicit write back */
	B	_f1tail

_return:
	MOVW	to+0(FP), R0
	RET

_bunaligned:
	CMP	$2, R(TMP)		/* is R(TMP) < 2 ? */

	MOVW.LT	$8, R(RSHIFT)		/* (R(n)<<24)|(R(n-1)>>8) */
	MOVW.LT	$24, R(LSHIFT)
	MOVW.LT	$1, R(OFFSET)

	MOVW.EQ	$16, R(RSHIFT)		/* (R(n)<<16)|(R(n-1)>>16) */
	MOVW.EQ	$16, R(LSHIFT)
	MOVW.EQ	$2, R(OFFSET)

	MOVW.GT	$24, R(RSHIFT)		/* (R(n)<<8)|(R(n-1)>>24) */
	MOVW.GT	$8, R(LSHIFT)
	MOVW.GT	$3, R(OFFSET)

	ADD	$16, R(TS), R(TMP)	/* do 16-byte chunks if possible */
	CMP	R(TMP), R(TE)
	BLS	_b1tail

	BIC	$3, R(FROM)		/* align source */
	MOVW	R(TS), savedts-4(SP)
	MOVW	(R(FROM)), R(BR0)	/* prime first block register */

_bu16loop:
	CMP	R(TMP), R(TE)
	BLS	_bu1tail

	MOVW	R(BR0)<<R(LSHIFT), R(BW3)
	MOVM.DB.W (R(FROM)), [R(BR0)-R(BR3)]
	ORR	R(BR3)>>R(RSHIFT), R(BW3)

	MOVW	R(BR3)<<R(LSHIFT), R(BW2)
	ORR	R(BR2)>>R(RSHIFT), R(BW2)

	MOVW	R(BR2)<<R(LSHIFT), R(BW1)
	ORR	R(BR1)>>R(RSHIFT), R(BW1)

	MOVW	R(BR1)<<R(LSHIFT), R(BW0)
	ORR	R(BR0)>>R(RSHIFT), R(BW0)

	MOVM.DB.W [R(BW0)-R(BW3)], (R(TE))
	B	_bu16loop

_bu1tail:
	MOVW	savedts-4(SP), R(TS)
	ADD	R(OFFSET), R(FROM)
	B	_b1tail

_funaligned:
	CMP	$2, R(TMP)

	MOVW.LT	$8, R(RSHIFT)		/* (R(n+1)<<24)|(R(n)>>8) */
	MOVW.LT	$24, R(LSHIFT)
	MOVW.LT	$3, R(OFFSET)

	MOVW.EQ	$16, R(RSHIFT)		/* (R(n+1)<<16)|(R(n)>>16) */
	MOVW.EQ	$16, R(LSHIFT)
	MOVW.EQ	$2, R(OFFSET)

	MOVW.GT	$24, R(RSHIFT)		/* (R(n+1)<<8)|(R(n)>>24) */
	MOVW.GT	$8, R(LSHIFT)
	MOVW.GT	$1, R(OFFSET)

	SUB	$16, R(TE), R(TMP)	/* do 16-byte chunks if possible */
	CMP	R(TMP), R(TS)
	BHS	_f1tail

	BIC	$3, R(FROM)		/* align source */
	MOVW	R(TE), savedte-4(SP)
	MOVW.P	4(R(FROM)), R(FR3)	/* prime last block register, implicit write back */

_fu16loop:
	CMP	R(TMP), R(TS)
	BHS	_fu1tail

	MOVW	R(FR3)>>R(RSHIFT), R(FW0)
	MOVM.IA.W (R(FROM)), [R(FR0),R(FR1),R(FR2),R(FR3)]
	ORR	R(FR0)<<R(LSHIFT), R(FW0)

	MOVW	R(FR0)>>R(RSHIFT), R(FW1)
	ORR	R(FR1)<<R(LSHIFT), R(FW1)

	MOVW	R(FR1)>>R(RSHIFT), R(FW2)
	ORR	R(FR2)<<R(LSHIFT), R(FW2)

	MOVW	R(FR2)>>R(RSHIFT), R(FW3)
	ORR	R(FR3)<<R(LSHIFT), R(FW3)

	MOVM.IA.W [R(FW0),R(FW1),R(FW2),R(FW3)], (R(TS))
	B	_fu16loop

_fu1tail:
	MOVW	savedte-4(SP), R(TE)
	SUB	R(OFFSET), R(FROM)
	B	_f1tail
