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