/* memcmp - compare memory

   Copyright (C) 2013-2020 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/>.  */

#include <sysdep.h>

/* Assumptions:
 *
 * ARMv8-a, AArch64, unaligned accesses.
 */

/* Parameters and result.  */
#define src1		x0
#define src2		x1
#define limit		x2
#define result		w0

/* Internal variables.  */
#define data1		x3
#define data1w		w3
#define data1h		x4
#define data2		x5
#define data2w		w5
#define data2h		x6
#define tmp1		x7
#define tmp2		x8

ENTRY_ALIGN (memcmp, 6)
	DELOUSE (0)
	DELOUSE (1)
	DELOUSE (2)

	subs	limit, limit, 16
	b.lo	L(less16)

	ldp	data1, data1h, [src1], 16
	ldp	data2, data2h, [src2], 16
	ccmp	data1, data2, 0, ne
	ccmp	data1h, data2h, 0, eq
	b.ne	L(return64)

	subs	limit, limit, 16
	b.ls	L(last_bytes)
	cmp	limit, 112
	b.lo	L(loop16)

	and	tmp1, src1, 15
	add	limit, limit, tmp1
	sub	src1, src1, tmp1
	sub	src2, src2, tmp1
	subs	limit, limit, 48

	/* Compare 128 up bytes using aligned access. */
	.p2align 4
L(loop64):
	ldp	data1, data1h, [src1]
	ldp	data2, data2h, [src2]
	cmp	data1, data2
	ccmp	data1h, data2h, 0, eq
	b.ne	L(return64)

	ldp	data1, data1h, [src1, 16]
	ldp	data2, data2h, [src2, 16]
	cmp	data1, data2
	ccmp	data1h, data2h, 0, eq
	b.ne	L(return64)

	ldp	data1, data1h, [src1, 32]
	ldp	data2, data2h, [src2, 32]
	cmp	data1, data2
	ccmp	data1h, data2h, 0, eq
	b.ne	L(return64)

	ldp	data1, data1h, [src1, 48]
	ldp	data2, data2h, [src2, 48]
	cmp	data1, data2
	ccmp	data1h, data2h, 0, eq
	b.ne	L(return64)

	subs	limit, limit, 64
	add	src1, src1, 64
	add	src2, src2, 64
	b.pl	L(loop64)
	adds	limit, limit, 48
	b.lo	L(last_bytes)

L(loop16):
	ldp	data1, data1h, [src1], 16
	ldp	data2, data2h, [src2], 16
	cmp	data1, data2
	ccmp	data1h, data2h, 0, eq
	b.ne	L(return64)

	subs	limit, limit, 16
	b.hi	L(loop16)
	/* Compare last 1-16 bytes using unaligned access.  */
L(last_bytes):
	add	src1, src1, limit
	add	src2, src2, limit
	ldp	data1, data1h, [src1]
	ldp	data2, data2h, [src2]

	/* Compare data bytes and set return value to 0, -1 or 1.  */
L(return64):
	cmp	data1, data2
	csel	data1, data1, data1h, ne
	csel	data2, data2, data2h, ne
L(return):
#ifndef __AARCH64EB__
	rev	data1, data1
	rev	data2, data2
#endif
	cmp	data1, data2
	cset	result, ne
	cneg	result, result, lo
	ret

	.p2align 4
L(less16):
	adds	limit, limit, 8
	b.lo	L(less8)		//lo:<
	ldr	data1, [src1]
	ldr	data2, [src2]
	/* equal 8 optimized */
	ccmp	data1, data2, 0, ne
	b.ne	L(return)

	ldr	data1, [src1, limit]
	ldr	data2, [src2, limit]
	b	L(return)

	.p2align 4
L(less8):
	adds	limit, limit, 4
	b.lo	L(less4)
	ldr	data1w, [src1]
	ldr	data2w, [src2]
	ccmp	data1w, data2w, 0, ne
	b.ne	L(return)
	ldr	data1w,	[src1, limit]
	ldr	data2w,	[src2, limit]
	b	L(return)

	.p2align 4
L(less4):
	adds	limit, limit, 4
	b.eq	L(ret_0)

L(byte_loop):
	ldrb	data1w, [src1], 1
	ldrb	data2w, [src2], 1
	subs	limit, limit, 1
	ccmp	data1w, data2w, 0, ne	/* NZCV = 0b0000.  */
	b.eq	L(byte_loop)
	sub	result, data1w, data2w
	ret
L(ret_0):
	mov	result, 0
	ret

END (memcmp)
#undef bcmp
weak_alias (memcmp, bcmp)
libc_hidden_builtin_def (memcmp)
