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) |
133 | SYM_FUNC_ALIAS(__pi_strlen, strlen) |
134 | |