From 450fe1bfa32643f7be38dbad89c8d32a539d5b66 Mon Sep 17 00:00:00 2001
From: Marcus Shawcroft <marcus.shawcroft@arm.com>
Date: Thu, 17 Jan 2013 14:53:32 +0000
Subject: [PATCH] 2013-01-17  Marcus Shawcroft  <marcus.shawcroft@linaro.org>

	    * libc/machine/aarch64/strncmp.S: Correct arithmetic for
	    argument N values close to the maximum representable
	    value in an unsigned 64 bit value.
---
 newlib/ChangeLog                      |  6 ++++++
 newlib/libc/machine/aarch64/strncmp.S | 31 ++++++++++++++++-----------
 2 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/newlib/ChangeLog b/newlib/ChangeLog
index 15123c0fc..d65758498 100644
--- a/newlib/ChangeLog
+++ b/newlib/ChangeLog
@@ -1,3 +1,9 @@
+2013-01-17  Marcus Shawcroft  <marcus.shawcroft@linaro.org>
+
+	* libc/machine/aarch64/strncmp.S: Correct arithmetic for
+	argument N values close to the maximum representable
+	value in an unsigned 64 bit value.
+
 2013-01-17  Marcus Shawcroft  <marcus.shawcroft@linaro.org>
 
 	* libc/machine/aarch64/strnlen.S: Correct arithmetic for
diff --git a/newlib/libc/machine/aarch64/strncmp.S b/newlib/libc/machine/aarch64/strncmp.S
index 6c2246dfb..0b90dd8a6 100644
--- a/newlib/libc/machine/aarch64/strncmp.S
+++ b/newlib/libc/machine/aarch64/strncmp.S
@@ -81,8 +81,10 @@ def_fn strncmp
 	b.ne	.Lmisaligned8
 	ands	tmp1, src1, #7
 	b.ne	.Lmutual_align
-	add	limit_wd, limit, #7
-	lsr	limit_wd, limit_wd, #3
+	/* Calculate the number of full and partial words -1.  */
+	sub	limit_wd, limit, #1	/* limit != 0, so no underflow.  */
+	lsr	limit_wd, limit_wd, #3	/* Convert to Dwords.  */
+
 	/* NUL detection works on the principle that (X - 1) & (~X) & 0x80
 	   (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
 	   can be done in parallel across the entire word.  */
@@ -95,14 +97,14 @@ def_fn strncmp
 	sub	tmp1, data1, zeroones
 	orr	tmp2, data1, #REP8_7f
 	eor	diff, data1, data2	/* Non-zero if differences found.  */
-	csinv	endloop, diff, xzr, ne	/* Last Dword or differences.  */
+	csinv	endloop, diff, xzr, pl	/* Last Dword or differences.  */
 	bics	has_nul, tmp1, tmp2	/* Non-zero if NUL terminator.  */
 	ccmp	endloop, #0, #0, eq
 	b.eq	.Lloop_aligned
 	/* End of performance-critical section  -- one 64B cache line.  */
 
 	/* Not reached the limit, must have found the end or a diff.  */
-	cbnz	limit_wd, .Lnot_limit
+	tbz	limit_wd, #63, .Lnot_limit
 
 	/* Limit % 8 == 0 => all bytes significant.  */
 	ands	limit, limit, #7
@@ -177,26 +179,31 @@ def_fn strncmp
 .Lmutual_align:
 	/* Sources are mutually aligned, but are not currently at an
 	   alignment boundary.  Round down the addresses and then mask off
-	   the bytes that precede the start point.  */
+	   the bytes that precede the start point.
+	   We also need to adjust the limit calculations, but without
+	   overflowing if the limit is near ULONG_MAX.  */
 	bic	src1, src1, #7
 	bic	src2, src2, #7
-	add	limit, limit, tmp1	/* Adjust the limit for the extra.  */
-	lsl	tmp1, tmp1, #3		/* Bytes beyond alignment -> bits.  */
 	ldr	data1, [src1], #8
-	neg	tmp1, tmp1		/* Bits to alignment -64.  */
+	neg	tmp3, tmp1, lsl #3	/* 64 - bits(bytes beyond align). */
 	ldr	data2, [src2], #8
 	mov	tmp2, #~0
+	sub	limit_wd, limit, #1	/* limit != 0, so no underflow.  */
 #ifdef __AARCH64EB__
 	/* Big-endian.  Early bytes are at MSB.  */
-	lsl	tmp2, tmp2, tmp1	/* Shift (tmp1 & 63).  */
+	lsl	tmp2, tmp2, tmp3	/* Shift (tmp1 & 63).  */
 #else
 	/* Little-endian.  Early bytes are at LSB.  */
-	lsr	tmp2, tmp2, tmp1	/* Shift (tmp1 & 63).  */
+	lsr	tmp2, tmp2, tmp3	/* Shift (tmp1 & 63).  */
 #endif
-	add	limit_wd, limit, #7
+	and	tmp3, limit_wd, #7
+	lsr	limit_wd, limit_wd, #3
+	/* Adjust the limit. Only low 3 bits used, so overflow irrelevant.  */
+	add	limit, limit, tmp1
+	add	tmp3, tmp3, tmp1
 	orr	data1, data1, tmp2
 	orr	data2, data2, tmp2
-	lsr	limit_wd, limit_wd, #3
+	add	limit_wd, limit_wd, tmp3, lsr #3
 	b	.Lstart_realigned
 
 .Lret0: