/* Copyright (C) 2000-2023 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   sw6 optimized by Rick Gorton <rick.gorton@sw_64-processor.com>.

   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
   <http://www.gnu.org/licenses/>.  */

/*
 * Much of the information about 21264 scheduling/coding comes from:
 *	Compiler Writer's Guide for the Sw_64 21264
 *	abbreviated as 'CWG' in other comments here
 *	ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
 * Scheduling notation:
 *	E	- either cluster
 *	U	- upper subcluster; U0 - subcluster U0; U1 - subcluster U1
 *	L	- lower subcluster; L0 - subcluster L0; L1 - subcluster L1
 *
 * Temp usage notes:
 *	$0		- destination address
 *	$1,$2,		- scratch
 */

#include <sysdep.h>

	.set noreorder
	.set noat
	.align  4


	.type $jmppointh,@object
$jumppointh:
        .gprel32        $both_0mod8
        .gprel32        J$H01
        .gprel32        J$H02
        .gprel32        J$H03
        .gprel32        J$H04
        .gprel32        J$H05
        .gprel32        J$H06
        .gprel32        J$H07

ENTRY(memcpy)
#memcpy:
	.prologue 1

	ldgp	$29, 0($27)

	mov	$16, $0			# E : copy dest to return
#	mov	$16, $1
	ble	$18, $nomoredata	# U : done with the copy?
        cmplt	$18, 8, $1
        bne 	$1, $less_8

	/* source and dest are same mod 8 address */
	and	$16, 7, $1		# E : Are both 0mod8?
	beq	$1, $both_0mod8		# U : Yes
	nop				# E :

	/*
	 * source and dest are same misalignment.  move a byte at a time
	 * until a 0mod8 alignment for both is reached.
	 * At least one byte more to move
	 */

        ldi   $2, 8
        subl  $2, $1, $1

$head_align:
        addl   $16, $1, $16
        addl   $17, $1, $17
        subl   $18, $1, $18
        ldih    $2, $jumppointh($29)    !gprelhigh
        s4addl $1, $2, $2
        ldw    $2, $jumppointh($2)      !gprellow
        addl   $2, $29, $2
        jmp    ($2)

$both_0mod8:
	cmple	$18, 127, $1		# E : Can we unroll the loop?
	bne	$1, $no_unroll		# U :


$do_unroll:
	ldih	$1, 8($31)		# big than 512K
	cmple	$18, $1, $1
	beq	$1, $unroll_body_512
	nop
	nop

$unroll_body:
	ldl	$6, 0($17)		# L0 : bytes 0..7
	nop				# E :
	nop				# E :

	ldl	$4, 8($17)		# L : bytes 8..15
	ldl	$5, 16($17)		# L : bytes 16..23
	nop				# E :
	nop				# E :

	ldl	$3, 24($17)		# L : bytes 24..31
	nop				# E :
	nop				# E :

	stl	$6, 0($16)		# L : bytes 0..7
	nop				# E :
	nop				# E :

	stl	$4, 8($16)		# L : bytes 8..15
	stl	$5, 16($16)		# L : bytes 16..23
	nop				# E :

	stl	$3, 24($16)		# L : bytes 24..31
	nop				# E :
	nop				# E :

	ldl	$22, 32($17)		# L : bytes 0..7
	ldl	$23, 40($17)		# L : bytes 8..15
					# fallback wh64 address if < 2 more trips
	nop				# E :
	nop				# E :

	ldl	$24, 48($17)		# L : bytes 16..23
	ldl	$25, 56($17)		# L : bytes 24..31
	stl	$22, 32($16)		# L : bytes 0..7
	stl	$23, 40($16)		# L : bytes 8..15
	stl	$24, 48($16)		# L : bytes 16..23
	stl	$25, 56($16)		# L : bytes 24..31
	addl	$17, 64, $17		# E : src += 32 bytes
	addl	$16, 64, $16		# E : dest += 32
	subl	$18, 64, $18		# E : count -= 64


	nop				# E :
	cmple	$18, 63, $1		# E : At least one more trip?
	beq	$1, $unroll_body
	nop
	nop
	nop
	br	$tail_quads

$unroll_body_512:
#	fillcs	128*4($17)
	e_fillcs 128*20($17)  #org

	ldl     $6, 0($17)              # L0 : bytes 0..7
        nop                             # E :
        nop                             # E :

        ldl     $4, 8($17)              # L : bytes 8..15
        ldl     $5, 16($17)             # L : bytes 16..23
        nop                             # E :
        nop                             # E :

        ldl     $3, 24($17)             # L : bytes 24..31
        nop                             # E :
        nop                             # E :

        stl     $6, 0($16)              # L : bytes 0..7
        nop                             # E :
        nop                             # E :

        stl     $4, 8($16)              # L : bytes 8..15
        stl     $5, 16($16)             # L : bytes 16..23
        nop                             # E :

        stl     $3, 24($16)             # L : bytes 24..31
        nop                             # E :
        nop                             # E :

	nop                             # E :
        nop                             # E :

        ldl     $22, 32($17)            # L : bytes 0..7
        ldl     $23, 40($17)            # L : bytes 8..15
                                        # fallback wh64 address if < 2 more trips
        nop                             # E :
        nop                             # E :

        ldl     $24, 48($17)            # L : bytes 16..23
        ldl     $25, 56($17)            # L : bytes 24..31
        stl     $22, 32($16)            # L : bytes 0..7
        stl     $23, 40($16)            # L : bytes 8..15
        stl     $24, 48($16)            # L : bytes 16..23
        stl     $25, 56($16)            # L : bytes 24..31
        addl    $17, 64, $17            # E : src += 32 bytes
        addl    $16, 64, $16            # E : dest += 32
        subl    $18, 64, $18            # E : count -= 64


        nop                             # E :
        cmple   $18, 63, $1             # E : At least one more trip?



//	e_fillcs 128*7($16)

	nop				# E :
	beq	$1, $unroll_body_512


$tail_quads:
$no_unroll:
	.align 4
	subl	$18, 8, $18		# E : At least a quad left?
	blt	$18, $less_than_8	# U : Nope
	nop				# E :
	nop				# E :

$move_a_quad:
	ldl	$1, 0($17)		# L : fetch 8
	subl	$18, 8, $18		# E : count -= 8
	addl	$17, 8, $17		# E : src += 8
	nop				# E :

	stl	$1, 0($16)		# L : store 8
	addl	$16, 8, $16		# E : dest += 8
	bge	$18, $move_a_quad	# U :
	nop				# E :

$less_than_8:
	.align 4
	addl	$18, 8, $18		# E : add back for trailing bytes
	ble	$18, $nomoredata	# U : All-done
	nop				# E :
	nop				# E :

	/* Trailing bytes */
$tail_bytes:
	subl	$18, 1, $18		# E : count--
	ldbu	$1, 0($17)		# L : fetch a byte
	addl	$17, 1, $17		# E : src++
	nop				# E :

	stb	$1, 0($16)		# L : store a byte
	addl	$16, 1, $16		# E : dest++
	bgt	$18, $tail_bytes	# U : more to be done?
	nop				# E :

	/* branching to exit takes 3 extra cycles, so replicate exit here */
	ret	$31, ($26), 1		# L0 :
	nop				# E :
	nop				# E :
	nop				# E :

$less_8:
	ldbu	$1, 0($17)		# L : fetch 1
	subl	$18, 1, $18		# E : count--
	addl	$17, 1, $17		# E : src++
	nop				# E :

	stb	$1, 0($16)		# L : store
	addl	$16, 1, $16		# E : dest++
	bgt	$18, $less_8		# U : more to go?
	nop

$nomoredata:
	ret	$31, ($26), 1		# L0 :
	nop				# E :
	nop				# E :
	nop				# E :

J$H01:
        ldbu    $1,-1($17)
        stb     $1,-1($16)
        br      $both_0mod8

J$H02:
        ldh     $1,-2($17)
        sth     $1,-2($16)
        br      $both_0mod8

J$H03:
        ldh     $1,-2($17)
        ldbu    $2,-3($17)
        sth     $1,-2($16)
        stb     $2,-3($16)
        br      $both_0mod8

J$H04:
        ldw     $1,-4($17)
        stw     $1,-4($16)
        br      $both_0mod8

J$H05:
        ldw     $1,-4($17)
        ldbu    $2,-5($17)
        stw     $1,-4($16)
        stb     $2,-5($16)
        br      $both_0mod8

J$H06:
        ldw     $1,-4($17)
        ldh     $2,-6($17)
        stw     $1,-4($16)
        sth     $2,-6($16)
        br      $both_0mod8

J$H07:
        ldw     $1,-4($17)
        ldh     $2,-6($17)
        ldbu    $3,-7($17)
        stw     $1,-4($16)
        sth     $2,-6($16)
        stb     $3,-7($16)
        br      $both_0mod8

END(memcpy)
libc_hidden_builtin_def (memcpy)

#	.end memcpy
