// Copyright 2022 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.

#include "go_asm.h"
#include "textflag.h"

TEXT ·Compare<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-56
#ifndef GOEXPERIMENT_regabiargs
	MOV	a_base+0(FP), X10
	MOV	a_len+8(FP), X11
	MOV	b_base+24(FP), X12
	MOV	b_len+32(FP), X13
	MOV	$ret+48(FP), X14
#else
	// X10 = a_base
	// X11 = a_len
	// X12 = a_cap (unused)
	// X13 = b_base (want in X12)
	// X14 = b_len (want in X13)
	// X15 = b_cap (unused)
	MOV	X13, X12
	MOV	X14, X13
#endif
	JMP	compare<>(SB)

TEXT runtime·cmpstring<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-40
#ifndef GOEXPERIMENT_regabiargs
	MOV	a_base+0(FP), X10
	MOV	a_len+8(FP), X11
	MOV	b_base+16(FP), X12
	MOV	b_len+24(FP), X13
	MOV	$ret+32(FP), X14
#endif
	// X10 = a_base
	// X11 = a_len
	// X12 = b_base
	// X13 = b_len
	JMP	compare<>(SB)

// On entry:
// X10 points to start of a
// X11 length of a
// X12 points to start of b
// X13 length of b
// for non-regabi X14 points to the address to store the return value (-1/0/1)
// for regabi the return value in X10
TEXT compare<>(SB),NOSPLIT|NOFRAME,$0
	BEQ	X10, X12, cmp_len

	MOV	X11, X5
	BGE	X13, X5, use_a_len // X5 = min(len(a), len(b))
	MOV	X13, X5
use_a_len:
	BEQZ	X5, cmp_len

	MOV	$32, X6
	BLT	X5, X6, loop4_check

	// Check alignment - if alignment differs we have to do one byte at a time.
	AND	$3, X10, X7
	AND	$3, X12, X8
	BNE	X7, X8, loop4_check
	BEQZ	X7, loop32_check

	// Check one byte at a time until we reach 8 byte alignment.
	SUB	X7, X5, X5
align:
	ADD	$-1, X7
	MOVBU	0(X10), X8
	MOVBU	0(X12), X9
	BNE	X8, X9, cmp
	ADD	$1, X10
	ADD	$1, X12
	BNEZ	X7, align

loop32_check:
	MOV	$32, X7
	BLT	X5, X7, loop16_check
loop32:
	MOV	0(X10), X15
	MOV	0(X12), X16
	MOV	8(X10), X17
	MOV	8(X12), X18
	BEQ	X15, X16, loop32a
	JMP	cmp8a
loop32a:
	BEQ	X17, X18, loop32b
	JMP	cmp8b
loop32b:
	MOV	16(X10), X15
	MOV	16(X12), X16
	MOV	24(X10), X17
	MOV	24(X12), X18
	BEQ	X15, X16, loop32c
	JMP	cmp8a
loop32c:
	BEQ	X17, X18, loop32d
	JMP	cmp8b
loop32d:
	ADD	$32, X10
	ADD	$32, X12
	ADD	$-32, X5
	BGE	X5, X7, loop32
	BEQZ	X5, cmp_len

loop16_check:
	MOV	$16, X6
	BLT	X5, X6, loop4_check
loop16:
	MOV	0(X10), X15
	MOV	0(X12), X16
	MOV	8(X10), X17
	MOV	8(X12), X18
	BEQ	X15, X16, loop16a
	JMP	cmp8a
loop16a:
	BEQ	X17, X18, loop16b
	JMP	cmp8b
loop16b:
	ADD	$16, X10
	ADD	$16, X12
	ADD	$-16, X5
	BGE	X5, X6, loop16
	BEQZ	X5, cmp_len

loop4_check:
	MOV	$4, X6
	BLT	X5, X6, loop1
loop4:
	MOVBU	0(X10), X8
	MOVBU	0(X12), X9
	MOVBU	1(X10), X15
	MOVBU	1(X12), X16
	BEQ	X8, X9, loop4a
	SLTU	X9, X8, X5
	SLTU	X8, X9, X6
	JMP	cmp_ret
loop4a:
	BEQ	X15, X16, loop4b
	SLTU	X16, X15, X5
	SLTU	X15, X16, X6
	JMP	cmp_ret
loop4b:
	MOVBU	2(X10), X21
	MOVBU	2(X12), X22
	MOVBU	3(X10), X23
	MOVBU	3(X12), X24
	BEQ	X21, X22, loop4c
	SLTU	X22, X21, X5
	SLTU	X21, X22, X6
	JMP	cmp_ret
loop4c:
	BEQ	X23, X24, loop4d
	SLTU	X24, X23, X5
	SLTU	X23, X24, X6
	JMP	cmp_ret
loop4d:
	ADD	$4, X10
	ADD	$4, X12
	ADD	$-4, X5
	BGE	X5, X6, loop4

loop1:
	BEQZ	X5, cmp_len
	MOVBU	0(X10), X8
	MOVBU	0(X12), X9
	BNE	X8, X9, cmp
	ADD	$1, X10
	ADD	$1, X12
	ADD	$-1, X5
	JMP	loop1

	// Compare 8 bytes of memory in X15/X16 that are known to differ.
cmp8a:
	MOV	$0xff, X19
cmp8a_loop:
	AND	X15, X19, X8
	AND	X16, X19, X9
	BNE	X8, X9, cmp
	SLLI	$8, X19
	JMP	cmp8a_loop

	// Compare 8 bytes of memory in X17/X18 that are known to differ.
cmp8b:
	MOV	$0xff, X19
cmp8b_loop:
	AND	X17, X19, X8
	AND	X18, X19, X9
	BNE	X8, X9, cmp
	SLLI	$8, X19
	JMP	cmp8b_loop

cmp_len:
	MOV	X11, X8
	MOV	X13, X9
cmp:
	SLTU	X9, X8, X5
	SLTU	X8, X9, X6
cmp_ret:
	SUB	X5, X6, X10
#ifndef GOEXPERIMENT_regabiargs
	MOV	X10, (X14)
#endif
	RET
