1/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <linux/linkage.h>
4#include <asm/asm.h>
5#include <asm/alternative-macros.h>
6#include <asm/hwcap.h>
7
8/* int strlen(const char *s) */
9SYM_FUNC_START(strlen)
10
11	ALTERNATIVE("nop", "j strlen_zbb", 0, RISCV_ISA_EXT_ZBB, CONFIG_RISCV_ISA_ZBB)
12
13	/*
14	 * Returns
15	 *   a0 - string length
16	 *
17	 * Parameters
18	 *   a0 - String to measure
19	 *
20	 * Clobbers:
21	 *   t0, t1
22	 */
23	mv	t1, a0
241:
25	lbu	t0, 0(t1)
26	beqz	t0, 2f
27	addi	t1, t1, 1
28	j	1b
292:
30	sub	a0, t1, a0
31	ret
32
33/*
34 * Variant of strlen using the ZBB extension if available
35 */
36#ifdef CONFIG_RISCV_ISA_ZBB
37strlen_zbb:
38
39#ifdef CONFIG_CPU_BIG_ENDIAN
40# define CZ	clz
41# define SHIFT	sll
42#else
43# define CZ	ctz
44# define SHIFT	srl
45#endif
46
47.option push
48.option arch,+zbb
49
50	/*
51	 * Returns
52	 *   a0 - string length
53	 *
54	 * Parameters
55	 *   a0 - String to measure
56	 *
57	 * Clobbers
58	 *   t0, t1, t2, t3
59	 */
60
61	/* Number of irrelevant bytes in the first word. */
62	andi	t2, a0, SZREG-1
63
64	/* Align pointer. */
65	andi	t0, a0, -SZREG
66
67	li	t3, SZREG
68	sub	t3, t3, t2
69	slli	t2, t2, 3
70
71	/* Get the first word.  */
72	REG_L	t1, 0(t0)
73
74	/*
75	 * Shift away the partial data we loaded to remove the irrelevant bytes
76	 * preceding the string with the effect of adding NUL bytes at the
77	 * end of the string's first word.
78	 */
79	SHIFT	t1, t1, t2
80
81	/* Convert non-NUL into 0xff and NUL into 0x00. */
82	orc.b	t1, t1
83
84	/* Convert non-NUL into 0x00 and NUL into 0xff. */
85	not	t1, t1
86
87	/*
88	 * Search for the first set bit (corresponding to a NUL byte in the
89	 * original chunk).
90	 */
91	CZ	t1, t1
92
93	/*
94	 * The first chunk is special: compare against the number
95	 * of valid bytes in this chunk.
96	 */
97	srli	a0, t1, 3
98	bgtu	t3, a0, 2f
99
100	/* Prepare for the word comparison loop. */
101	addi	t2, t0, SZREG
102	li	t3, -1
103
104	/*
105	 * Our critical loop is 4 instructions and processes data in
106	 * 4 byte or 8 byte chunks.
107	 */
108	.p2align 3
1091:
110	REG_L	t1, SZREG(t0)
111	addi	t0, t0, SZREG
112	orc.b	t1, t1
113	beq	t1, t3, 1b
114
115	not	t1, t1
116	CZ	t1, t1
117	srli	t1, t1, 3
118
119	/* Get number of processed bytes. */
120	sub	t2, t0, t2
121
122	/* Add number of characters in the first word.  */
123	add	a0, a0, t2
124
125	/* Add number of characters in the last word.  */
126	add	a0, a0, t1
1272:
128	ret
129
130.option pop
131#endif
132SYM_FUNC_END(strlen)
133SYM_FUNC_ALIAS(__pi_strlen, strlen)
134