1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Taken from Linux arch/riscv/lib/strlen.S
4 */
5
6#include <linux/linkage.h>
7#include <asm/asm.h>
8
9#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
10# define CZ	ctz
11# define SHIFT	srl
12#else
13# define CZ	clz
14# define SHIFT	sll
15#endif
16
17ENTRY(__strlen)
18WEAK(strlen)
19.option push
20.option arch,+zbb
21	/*
22	 * Returns
23	 *   a0 - string length
24	 *
25	 * Parameters
26	 *   a0 - String to measure
27	 *
28	 * Clobbers
29	 *   t0, t1, t2, t3
30	 */
31
32	/* Number of irrelevant bytes in the first word. */
33	andi	t2, a0, SZREG-1
34
35	/* Align pointer. */
36	andi	t0, a0, -SZREG
37
38	li	t3, SZREG
39	sub	t3, t3, t2
40	slli	t2, t2, 3
41
42	/* Get the first word.  */
43	REG_L	t1, 0(t0)
44
45	/*
46	 * Shift away the partial data we loaded to remove the irrelevant bytes
47	 * preceding the string with the effect of adding NUL bytes at the
48	 * end of the string's first word.
49	 */
50	SHIFT	t1, t1, t2
51
52	/* Convert non-NUL into 0xff and NUL into 0x00. */
53	orc.b	t1, t1
54
55	/* Convert non-NUL into 0x00 and NUL into 0xff. */
56	not	t1, t1
57
58	/*
59	 * Search for the first set bit (corresponding to a NUL byte in the
60	 * original chunk).
61	 */
62	CZ	t1, t1
63
64	/*
65	 * The first chunk is special: compare against the number
66	 * of valid bytes in this chunk.
67	 */
68	srli	a0, t1, 3
69	bgtu	t3, a0, 2f
70
71	/* Prepare for the word comparison loop. */
72	addi	t2, t0, SZREG
73	li	t3, -1
74
75	/*
76	 * Our critical loop is 4 instructions and processes data in
77	 * 4 byte or 8 byte chunks.
78	 */
79	.p2align 3
801:
81	REG_L	t1, SZREG(t0)
82	addi	t0, t0, SZREG
83	orc.b	t1, t1
84	beq	t1, t3, 1b
85
86	not	t1, t1
87	CZ	t1, t1
88	srli	t1, t1, 3
89
90	/* Get number of processed bytes. */
91	sub	t2, t0, t2
92
93	/* Add number of characters in the first word.  */
94	add	a0, a0, t2
95
96	/* Add number of characters in the last word.  */
97	add	a0, a0, t1
982:
99	ret
100.option pop
101END(__strlen)
102