/*
 * arch/alpha/lib/strlen_user.S
 *
 * Return the length of the string including the NUL terminator
 * (strlen+1) or zero if an error occurred.
 *
 * In places where it is critical to limit the processing time,
 * and the data is not trusted, strnlen_user() should be used.
 * It will return a value greater than its second argument if
 * that limit would be exceeded. This implementation is allowed
 * to access memory beyond the limit, but will not cross a page
 * boundary when doing so.
 */

#include <asm/regdef.h>


/* Allow an exception for an insn; exit if we get one.  */
#define EX(x,y...)			\
	99: x,##y;			\
	.section __ex_table,"a";	\
	.gprel32 99b;			\
	lda v0, $exception-99b(zero);	\
	.previous


	.set noreorder
	.set noat
	.text

	.globl __strlen_user
	.ent __strlen_user
	.frame sp, 0, ra

	.align 3
__strlen_user:
	ldah	a1, 32767(zero)	# do not use plain strlen_user() for strings
				# that might be almost 2 GB long; you should
				# be using strnlen_user() instead

	.globl __strnlen_user

	.align 3
__strnlen_user:
	ldgp	$29,0($27)	# we do exceptions -- we need the gp.
	.prologue 1

	EX( ldq_u t0, 0(a0) )	# load first quadword (a0 may be misaligned)
	lda     t1, -1(zero)
	insqh   t1, a0, t1
	andnot  a0, 7, v0
	or      t1, t0, t0
	subq	a0, 1, a0	# get our +1 for the return 
	cmpbge  zero, t0, t1	# t1 <- bitmask: bit i == 1 <==> i-th byte == 0
	subq	a1, 7, t2
	subq	a0, v0, t0
	bne     t1, $found

	addq	t2, t0, t2
	addq	a1, 1, a1

	.align 3
$loop:	ble	t2, $limit
	EX( ldq t0, 8(v0) )
	subq	t2, 8, t2
	addq    v0, 8, v0	# addr += 8
	cmpbge  zero, t0, t1
	beq     t1, $loop

$found:	negq    t1, t2		# clear all but least set bit
	and     t1, t2, t1

	and     t1, 0xf0, t2	# binary search for that set bit
	and	t1, 0xcc, t3
	and	t1, 0xaa, t4
	cmovne	t2, 4, t2
	cmovne	t3, 2, t3
	cmovne	t4, 1, t4
	addq	t2, t3, t2
	addq	v0, t4, v0
	addq	v0, t2, v0
	nop			# dual issue next two on ev4 and ev5
	subq    v0, a0, v0
$exception:
	ret

	.align 3		# currently redundant
$limit:
	subq	a1, t2, v0
	ret

	.end __strlen_user
