xref: /linux/arch/riscv/lib/strnlen.S (revision feff82eb5f4075d541990d0ba60dad14ea83ea9b)
1/* SPDX-License-Identifier: GPL-2.0-only */
2
3/*
4 * Base on arch/riscv/lib/strlen.S
5 *
6 * Copyright (C) Feng Jiang <jiangfeng@kylinos.cn>
7 */
8
9#include <linux/linkage.h>
10#include <asm/asm.h>
11#include <asm/alternative-macros.h>
12#include <asm/hwcap.h>
13
14/* size_t strnlen(const char *s, size_t count) */
15SYM_FUNC_START(strnlen)
16
17	__ALTERNATIVE_CFG("nop", "j strnlen_zbb", 0, RISCV_ISA_EXT_ZBB,
18		IS_ENABLED(CONFIG_RISCV_ISA_ZBB) && IS_ENABLED(CONFIG_TOOLCHAIN_HAS_ZBB))
19
20
21	/*
22	 * Returns
23	 *   a0 - String length
24	 *
25	 * Parameters
26	 *   a0 - String to measure
27	 *   a1 - Max length of string
28	 *
29	 * Clobbers
30	 *   t0, t1, t2
31	 */
32	addi	t1, a0, -1
33	add	t2, a0, a1
341:
35	addi	t1, t1, 1
36	beq	t1, t2, 2f
37	lbu	t0, 0(t1)
38	bnez	t0, 1b
392:
40	sub	a0, t1, a0
41	ret
42
43
44/*
45 * Variant of strnlen using the ZBB extension if available
46 */
47#if defined(CONFIG_RISCV_ISA_ZBB) && defined(CONFIG_TOOLCHAIN_HAS_ZBB)
48strnlen_zbb:
49
50#ifdef CONFIG_CPU_BIG_ENDIAN
51# define CZ	clz
52# define SHIFT	sll
53#else
54# define CZ	ctz
55# define SHIFT	srl
56#endif
57
58.option push
59.option arch,+zbb
60
61	/*
62	 * Returns
63	 *   a0 - String length
64	 *
65	 * Parameters
66	 *   a0 - String to measure
67	 *   a1 - Max length of string
68	 *
69	 * Clobbers
70	 *   t0, t1, t2, t3, t4
71	 */
72
73	/* If maxlen is 0, return 0. */
74	beqz	a1, 3f
75
76	/* Number of irrelevant bytes in the first word. */
77	andi	t2, a0, SZREG-1
78
79	/* Align pointer. */
80	andi	t0, a0, -SZREG
81
82	li	t3, SZREG
83	sub	t3, t3, t2
84	slli	t2, t2, 3
85
86	/* Aligned boundary. */
87	add	t4, a0, a1
88	andi	t4, t4, -SZREG
89
90	/* Get the first word.  */
91	REG_L	t1, 0(t0)
92
93	/*
94	 * Shift away the partial data we loaded to remove the irrelevant bytes
95	 * preceding the string with the effect of adding NUL bytes at the
96	 * end of the string's first word.
97	 */
98	SHIFT	t1, t1, t2
99
100	/* Convert non-NUL into 0xff and NUL into 0x00. */
101	orc.b	t1, t1
102
103	/* Convert non-NUL into 0x00 and NUL into 0xff. */
104	not	t1, t1
105
106	/*
107	 * Search for the first set bit (corresponding to a NUL byte in the
108	 * original chunk).
109	 */
110	CZ	t1, t1
111
112	/*
113	 * The first chunk is special: compare against the number
114	 * of valid bytes in this chunk.
115	 */
116	srli	a0, t1, 3
117
118	/* Limit the result by maxlen. */
119	minu	a0, a0, a1
120
121	bgtu	t3, a0, 2f
122
123	/* Prepare for the word comparison loop. */
124	addi	t2, t0, SZREG
125	li	t3, -1
126
127	/*
128	 * Our critical loop is 4 instructions and processes data in
129	 * 4 byte or 8 byte chunks.
130	 */
131	.p2align 3
1321:
133	REG_L	t1, SZREG(t0)
134	addi	t0, t0, SZREG
135	orc.b	t1, t1
136	bgeu	t0, t4, 4f
137	beq	t1, t3, 1b
1384:
139	not	t1, t1
140	CZ	t1, t1
141	srli	t1, t1, 3
142
143	/* Get number of processed bytes. */
144	sub	t2, t0, t2
145
146	/* Add number of characters in the first word.  */
147	add	a0, a0, t2
148
149	/* Add number of characters in the last word.  */
150	add	a0, a0, t1
151
152	/* Ensure the final result does not exceed maxlen. */
153	minu	a0, a0, a1
1542:
155	ret
1563:
157	mv	a0, a1
158	ret
159
160.option pop
161#endif
162SYM_FUNC_END(strnlen)
163SYM_FUNC_ALIAS(__pi_strnlen, strnlen)
164EXPORT_SYMBOL(strnlen)
165