db3d42d99b78a475f5a49cc1d8b6db1077f7df11
[platform/kernel/linux-starfive.git] / arch / riscv / lib / strlen.S
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) */
9 SYM_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
24 1:
25         lbu     t0, 0(t1)
26         beqz    t0, 2f
27         addi    t1, t1, 1
28         j       1b
29 2:
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
37 strlen_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
109 1:
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
127 2:
128         ret
129
130 .option pop
131 #endif
132 SYM_FUNC_END(strlen)