~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm64/lib/strlen.S

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /* SPDX-License-Identifier: GPL-2.0-only */
  2 /*
  3  * Copyright (c) 2013-2021, Arm Limited.
  4  *
  5  * Adapted from the original at:
  6  * https://github.com/ARM-software/optimized-routines/blob/98e4d6a5c13c8e54/string/aarch64/strlen.S
  7  */
  8 
  9 #include <linux/linkage.h>
 10 #include <asm/assembler.h>
 11 #include <asm/mte-def.h>
 12 
 13 /* Assumptions:
 14  *
 15  * ARMv8-a, AArch64, unaligned accesses, min page size 4k.
 16  */
 17 
 18 #define L(label) .L ## label
 19 
 20 /* Arguments and results.  */
 21 #define srcin           x0
 22 #define len             x0
 23 
 24 /* Locals and temporaries.  */
 25 #define src             x1
 26 #define data1           x2
 27 #define data2           x3
 28 #define has_nul1        x4
 29 #define has_nul2        x5
 30 #define tmp1            x4
 31 #define tmp2            x5
 32 #define tmp3            x6
 33 #define tmp4            x7
 34 #define zeroones        x8
 35 
 36         /* NUL detection works on the principle that (X - 1) & (~X) & 0x80
 37            (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
 38            can be done in parallel across the entire word. A faster check
 39            (X - 1) & 0x80 is zero for non-NUL ASCII characters, but gives
 40            false hits for characters 129..255.  */
 41 
 42 #define REP8_01 0x0101010101010101
 43 #define REP8_7f 0x7f7f7f7f7f7f7f7f
 44 #define REP8_80 0x8080808080808080
 45 
 46 /*
 47  * When KASAN_HW_TAGS is in use, memory is checked at MTE_GRANULE_SIZE
 48  * (16-byte) granularity, and we must ensure that no access straddles this
 49  * alignment boundary.
 50  */
 51 #ifdef CONFIG_KASAN_HW_TAGS
 52 #define MIN_PAGE_SIZE MTE_GRANULE_SIZE
 53 #else
 54 #define MIN_PAGE_SIZE 4096
 55 #endif
 56 
 57         /* Since strings are short on average, we check the first 16 bytes
 58            of the string for a NUL character.  In order to do an unaligned ldp
 59            safely we have to do a page cross check first.  If there is a NUL
 60            byte we calculate the length from the 2 8-byte words using
 61            conditional select to reduce branch mispredictions (it is unlikely
 62            strlen will be repeatedly called on strings with the same length).
 63 
 64            If the string is longer than 16 bytes, we align src so don't need
 65            further page cross checks, and process 32 bytes per iteration
 66            using the fast NUL check.  If we encounter non-ASCII characters,
 67            fallback to a second loop using the full NUL check.
 68 
 69            If the page cross check fails, we read 16 bytes from an aligned
 70            address, remove any characters before the string, and continue
 71            in the main loop using aligned loads.  Since strings crossing a
 72            page in the first 16 bytes are rare (probability of
 73            16/MIN_PAGE_SIZE ~= 0.4%), this case does not need to be optimized.
 74 
 75            AArch64 systems have a minimum page size of 4k.  We don't bother
 76            checking for larger page sizes - the cost of setting up the correct
 77            page size is just not worth the extra gain from a small reduction in
 78            the cases taking the slow path.  Note that we only care about
 79            whether the first fetch, which may be misaligned, crosses a page
 80            boundary.  */
 81 
 82 SYM_FUNC_START(__pi_strlen)
 83         and     tmp1, srcin, MIN_PAGE_SIZE - 1
 84         mov     zeroones, REP8_01
 85         cmp     tmp1, MIN_PAGE_SIZE - 16
 86         b.gt    L(page_cross)
 87         ldp     data1, data2, [srcin]
 88 #ifdef __AARCH64EB__
 89         /* For big-endian, carry propagation (if the final byte in the
 90            string is 0x01) means we cannot use has_nul1/2 directly.
 91            Since we expect strings to be small and early-exit,
 92            byte-swap the data now so has_null1/2 will be correct.  */
 93         rev     data1, data1
 94         rev     data2, data2
 95 #endif
 96         sub     tmp1, data1, zeroones
 97         orr     tmp2, data1, REP8_7f
 98         sub     tmp3, data2, zeroones
 99         orr     tmp4, data2, REP8_7f
100         bics    has_nul1, tmp1, tmp2
101         bic     has_nul2, tmp3, tmp4
102         ccmp    has_nul2, 0, 0, eq
103         beq     L(main_loop_entry)
104 
105         /* Enter with C = has_nul1 == 0.  */
106         csel    has_nul1, has_nul1, has_nul2, cc
107         mov     len, 8
108         rev     has_nul1, has_nul1
109         clz     tmp1, has_nul1
110         csel    len, xzr, len, cc
111         add     len, len, tmp1, lsr 3
112         ret
113 
114         /* The inner loop processes 32 bytes per iteration and uses the fast
115            NUL check.  If we encounter non-ASCII characters, use a second
116            loop with the accurate NUL check.  */
117         .p2align 4
118 L(main_loop_entry):
119         bic     src, srcin, 15
120         sub     src, src, 16
121 L(main_loop):
122         ldp     data1, data2, [src, 32]!
123 L(page_cross_entry):
124         sub     tmp1, data1, zeroones
125         sub     tmp3, data2, zeroones
126         orr     tmp2, tmp1, tmp3
127         tst     tmp2, zeroones, lsl 7
128         bne     1f
129         ldp     data1, data2, [src, 16]
130         sub     tmp1, data1, zeroones
131         sub     tmp3, data2, zeroones
132         orr     tmp2, tmp1, tmp3
133         tst     tmp2, zeroones, lsl 7
134         beq     L(main_loop)
135         add     src, src, 16
136 1:
137         /* The fast check failed, so do the slower, accurate NUL check.  */
138         orr     tmp2, data1, REP8_7f
139         orr     tmp4, data2, REP8_7f
140         bics    has_nul1, tmp1, tmp2
141         bic     has_nul2, tmp3, tmp4
142         ccmp    has_nul2, 0, 0, eq
143         beq     L(nonascii_loop)
144 
145         /* Enter with C = has_nul1 == 0.  */
146 L(tail):
147 #ifdef __AARCH64EB__
148         /* For big-endian, carry propagation (if the final byte in the
149            string is 0x01) means we cannot use has_nul1/2 directly.  The
150            easiest way to get the correct byte is to byte-swap the data
151            and calculate the syndrome a second time.  */
152         csel    data1, data1, data2, cc
153         rev     data1, data1
154         sub     tmp1, data1, zeroones
155         orr     tmp2, data1, REP8_7f
156         bic     has_nul1, tmp1, tmp2
157 #else
158         csel    has_nul1, has_nul1, has_nul2, cc
159 #endif
160         sub     len, src, srcin
161         rev     has_nul1, has_nul1
162         add     tmp2, len, 8
163         clz     tmp1, has_nul1
164         csel    len, len, tmp2, cc
165         add     len, len, tmp1, lsr 3
166         ret
167 
168 L(nonascii_loop):
169         ldp     data1, data2, [src, 16]!
170         sub     tmp1, data1, zeroones
171         orr     tmp2, data1, REP8_7f
172         sub     tmp3, data2, zeroones
173         orr     tmp4, data2, REP8_7f
174         bics    has_nul1, tmp1, tmp2
175         bic     has_nul2, tmp3, tmp4
176         ccmp    has_nul2, 0, 0, eq
177         bne     L(tail)
178         ldp     data1, data2, [src, 16]!
179         sub     tmp1, data1, zeroones
180         orr     tmp2, data1, REP8_7f
181         sub     tmp3, data2, zeroones
182         orr     tmp4, data2, REP8_7f
183         bics    has_nul1, tmp1, tmp2
184         bic     has_nul2, tmp3, tmp4
185         ccmp    has_nul2, 0, 0, eq
186         beq     L(nonascii_loop)
187         b       L(tail)
188 
189         /* Load 16 bytes from [srcin & ~15] and force the bytes that precede
190            srcin to 0x7f, so we ignore any NUL bytes before the string.
191            Then continue in the aligned loop.  */
192 L(page_cross):
193         bic     src, srcin, 15
194         ldp     data1, data2, [src]
195         lsl     tmp1, srcin, 3
196         mov     tmp4, -1
197 #ifdef __AARCH64EB__
198         /* Big-endian.  Early bytes are at MSB.  */
199         lsr     tmp1, tmp4, tmp1        /* Shift (tmp1 & 63).  */
200 #else
201         /* Little-endian.  Early bytes are at LSB.  */
202         lsl     tmp1, tmp4, tmp1        /* Shift (tmp1 & 63).  */
203 #endif
204         orr     tmp1, tmp1, REP8_80
205         orn     data1, data1, tmp1
206         orn     tmp2, data2, tmp1
207         tst     srcin, 8
208         csel    data1, data1, tmp4, eq
209         csel    data2, data2, tmp2, eq
210         b       L(page_cross_entry)
211 SYM_FUNC_END(__pi_strlen)
212 SYM_FUNC_ALIAS_WEAK(strlen, __pi_strlen)
213 EXPORT_SYMBOL_NOKASAN(strlen)

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php