strcpy_arm.S revision 1.6 1 1.1 matt /*-
2 1.1 matt * Copyright (c) 2013 The NetBSD Foundation, Inc.
3 1.1 matt * All rights reserved.
4 1.1 matt *
5 1.1 matt * This code is derived from software contributed to The NetBSD Foundation
6 1.1 matt * by Matt Thomas of 3am Software Foundry.
7 1.1 matt *
8 1.1 matt * Redistribution and use in source and binary forms, with or without
9 1.1 matt * modification, are permitted provided that the following conditions
10 1.1 matt * are met:
11 1.1 matt * 1. Redistributions of source code must retain the above copyright
12 1.1 matt * notice, this list of conditions and the following disclaimer.
13 1.1 matt * 2. Redistributions in binary form must reproduce the above copyright
14 1.1 matt * notice, this list of conditions and the following disclaimer in the
15 1.1 matt * documentation and/or other materials provided with the distribution.
16 1.1 matt *
17 1.1 matt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 1.1 matt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 1.1 matt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 1.1 matt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 1.1 matt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 1.1 matt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 1.1 matt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 1.1 matt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 1.1 matt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 1.1 matt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 1.1 matt * POSSIBILITY OF SUCH DAMAGE.
28 1.1 matt */
29 1.1 matt
30 1.1 matt #include <machine/asm.h>
31 1.1 matt
32 1.6 christos RCSID("$NetBSD: strcpy_arm.S,v 1.6 2017/01/14 03:35:21 christos Exp $")
33 1.1 matt
34 1.1 matt #ifdef STRLCPY
35 1.1 matt #ifdef _LIBC
36 1.1 matt WEAK_ALIAS(strlcpy, _strlcpy)
37 1.6 christos # define FUNCNAME _strlcpy
38 1.6 christos # else
39 1.6 christos # define FUNCNAME strlcpy
40 1.6 christos # endif
41 1.1 matt #elif defined(STRNCPY)
42 1.6 christos # ifdef _LIBC
43 1.5 christos WEAK_ALIAS(strncpy, _strncpy)
44 1.6 christos # define FUNCNAME _strncpy
45 1.6 christos # else
46 1.6 christos # define FUNCNAME strncpy
47 1.6 christos # endif
48 1.1 matt #else
49 1.6 christos # ifdef _LIBC
50 1.5 christos WEAK_ALIAS(strcpy, _strcpy)
51 1.6 christos # define FUNCNAME _strcpy
52 1.6 christos # else
53 1.6 christos # define FUNCNAME strcpy
54 1.6 christos # endif
55 1.1 matt #endif
56 1.1 matt
57 1.1 matt #ifdef __ARMEL__
58 1.1 matt #define lslo lsr /* shift to lower address */
59 1.1 matt #define lshi lsl /* shift to higher address */
60 1.1 matt #define BYTE0 0x000000ff
61 1.1 matt #define BYTE1 0x0000ff00
62 1.1 matt #define BYTE2 0x00ff0000
63 1.1 matt #define BYTE3 0xff000000
64 1.1 matt #else
65 1.1 matt #define lslo lsl /* shift to lower address */
66 1.1 matt #define lshi lsr /* shift to higher address */
67 1.1 matt #define BYTE0 0xff000000
68 1.1 matt #define BYTE1 0x00ff0000
69 1.1 matt #define BYTE2 0x0000ff00
70 1.1 matt #define BYTE3 0x000000ff
71 1.1 matt #endif
72 1.1 matt
73 1.1 matt /*
74 1.1 matt * On armv6 and later, to quickly determine if a word contains a NUL (0) byte,
75 1.1 matt * we add 254 to each byte using the UQADD8 (unsigned saturating add 8)
76 1.1 matt * instruction. For every non-NUL byte, the result for that byte will become
77 1.1 matt * 255. For NUL, it will be 254. When we complement the result of all 4 adds,
78 1.1 matt * if the result is non-0 then we must have encountered a NUL.
79 1.1 matt *
80 1.1 matt * For earlier architecture, we just use tst on all 4 bytes. There are other
81 1.1 matt * algorithms to detect NULs but they take longer and use more instructions.
82 1.1 matt */
83 1.1 matt
84 1.1 matt /*
85 1.1 matt * char *strcpy(char *dst, const char *src);
86 1.1 matt * char *strncpy(char *dst, const char *src, size_t len);
87 1.1 matt * size_t strlcpy(char *dst, const char *src, size_t len);
88 1.1 matt */
89 1.1 matt
90 1.1 matt .text
91 1.1 matt ENTRY(FUNCNAME)
92 1.1 matt #if defined(STRLCPY)
93 1.1 matt cmp r2, #1 /* is length 1 or less? */
94 1.1 matt bhi 1f /* no, do normal */
95 1.1 matt moveq r3, #0 /* = 1? load NUL */
96 1.3 matt strbeq r3, [r0] /* = 1? write NUL to dst */
97 1.1 matt mov r0, r1 /* move src to r0 */
98 1.1 matt b PLT_SYM(_C_LABEL(strlen)) /* and tailcall strlen */
99 1.1 matt 1:
100 1.1 matt sub r2, r2, #1 /* leave one byte for NUL */
101 1.1 matt #endif
102 1.1 matt #if defined(STRNCPY)
103 1.1 matt cmp r2, #0 /* 0 length? */
104 1.1 matt RETc(eq) /* yes, just return */
105 1.1 matt #endif
106 1.1 matt push {r4-r9} /* save some registers */
107 1.1 matt #ifdef _ARM_ARCH_6
108 1.1 matt #ifdef _ARM_ARCH_7
109 1.1 matt movw r7, #0xfefe /* magic constant; 254 in each byte */
110 1.1 matt #else
111 1.1 matt mov r7, #0xfe /* put 254 in low byte */
112 1.1 matt orr r7, r7, r7, lsl #8 /* move to next byte */
113 1.1 matt #endif
114 1.1 matt orr r7, r7, r7, lsl #16 /* move to next halfword */
115 1.1 matt #endif
116 1.1 matt
117 1.1 matt #if defined(STRLCPY)
118 1.1 matt add r6, r1, #1 /* save for return (deal with NUL) */
119 1.1 matt #else
120 1.1 matt mov r6, r0 /* save for return */
121 1.1 matt #endif
122 1.1 matt
123 1.1 matt .Ldst_align:
124 1.1 matt tst r0, #3 /* check for dst alignment */
125 1.1 matt beq .Ldst_aligned /* ok, proceed to next check */
126 1.1 matt ldrb r5, [r1], #1 /* load a byte */
127 1.1 matt #if defined(STRNCPY)
128 1.1 matt subs r2, r2, #1 /* subtract out from count */
129 1.1 matt bmi .Ldst_full /* zero? the dst has no more room */
130 1.1 matt #endif
131 1.1 matt strb r5, [r0], #1 /* store a byte */
132 1.1 matt teq r5, #0 /* was it a NUL? */
133 1.1 matt beq .Lend_of_string /* yes, we are done */
134 1.1 matt #if defined(STRLCPY)
135 1.1 matt subs r2, r2, #1 /* subtract one from count */
136 1.3 matt strbeq r2, [r0], #1 /* zero? write trailing NUL */
137 1.1 matt beq .Ldst_full /* zero? the dst has no more room */
138 1.1 matt #endif
139 1.1 matt b .Ldst_align /* loop around for next byte */
140 1.1 matt .Ldst_aligned:
141 1.1 matt tst r1, #3 /* get the misalignment of src */
142 1.1 matt bne .Lincongruent /* !=? incongruent (slower) */
143 1.1 matt
144 1.1 matt /* =? congruent (faster) */
145 1.1 matt
146 1.1 matt .Lcongruent:
147 1.1 matt #if defined(STRLCPY)
148 1.1 matt add r6, r6, #3 /* compensate for word post-inc */
149 1.1 matt #endif
150 1.1 matt b .Lcongruent_mainloop_load
151 1.1 matt .Lcongruent_mainloop:
152 1.1 matt #if defined(STRLCPY) || defined(STRNCPY)
153 1.1 matt subs r2, r2, #4 /* subtract 4 from the count */
154 1.1 matt bmi .Lno_more_room
155 1.1 matt #endif
156 1.1 matt str r5, [r0], #4 /* store word into dst */
157 1.1 matt #if defined(STRLCPY)
158 1.1 matt beq .Lno_more_room /* count is 0? no room in dst */
159 1.1 matt #endif
160 1.1 matt #if defined(STRNCPY)
161 1.1 matt beq .Ldst_full_word_aligned /* count is 0? no room in dst */
162 1.1 matt #endif
163 1.1 matt .Lcongruent_mainloop_load:
164 1.1 matt ldr r5, [r1], #4 /* load word from source */
165 1.1 matt #if defined(_ARM_ARCH_6)
166 1.1 matt uqadd8 r3, r5, r7 /* magic happens here */
167 1.1 matt mvns r3, r3 /* is the complemented result 0? */
168 1.1 matt beq .Lcongruent_mainloop /* yes, no NULs, do it again */
169 1.1 matt #else
170 1.1 matt tst r5, #BYTE0 /* does byte 0 contain a NUL? */
171 1.1 matt tstne r5, #BYTE1 /* no, does byte 1 contain a NUL? */
172 1.1 matt tstne r5, #BYTE2 /* no, does byte 2 contain a NUL? */
173 1.1 matt tstne r5, #BYTE3 /* no, does byte 3 contain a NUL? */
174 1.1 matt bne .Lcongruent_mainloop /* yes, no NULs, do it again */
175 1.1 matt #endif
176 1.1 matt #if defined(STRLCPY) && 0
177 1.1 matt sub r1, r1, #3 /* back up src pointer */
178 1.1 matt #endif
179 1.1 matt #if defined(_ARM_ARCH_6)
180 1.1 matt #ifdef __ARMEL__
181 1.1 matt rev r3, r3 /* CLZ needs BE data */
182 1.1 matt #endif
183 1.1 matt clz r3, r3 /* count leading zeros */
184 1.1 matt #else
185 1.1 matt mov r3, #0 /* assume NUL is in byte 0 */
186 1.1 matt tst r5, #BYTE0 /* is NUL in byte 2? */
187 1.1 matt beq .Lcongruent_last_bytes /* yes, done searching. */
188 1.1 matt mov r3, #8 /* assume NUL is in byte 1 */
189 1.1 matt tst r5, #BYTE1 /* is NUL in byte 2? */
190 1.1 matt beq .Lcongruent_last_bytes /* yes, done searching. */
191 1.1 matt mov r3, #16 /* assume NUL is in byte 2 */
192 1.1 matt tst r5, #BYTE2 /* is NUL in byte 2? */
193 1.1 matt #if !defined(STRLCPY)
194 1.1 matt beq .Lcongruent_last_bytes /* yes, done searching. */
195 1.1 matt mov r3, #24 /* NUL must be in byte 3 */
196 1.1 matt #else
197 1.1 matt movne r3, #24 /* no, then NUL is in byte 3 */
198 1.1 matt #endif
199 1.1 matt #endif /* _ARM_ARCH_6 */
200 1.1 matt #if defined(STRLCPY)
201 1.1 matt .Lcongruent_last_bytes:
202 1.1 matt #endif
203 1.1 matt #if defined(STRLCPY)
204 1.1 matt add r1, r1, r3, lsr #3 /* position to point at NUL + 4 */
205 1.1 matt #endif
206 1.1 matt b .Llast_bytes /* store the last bytes */
207 1.1 matt
208 1.1 matt
209 1.1 matt .Lincongruent:
210 1.1 matt /*
211 1.1 matt * At this point dst is word aligned by src is not. Read bytes
212 1.1 matt * from src until it is read aligned.
213 1.1 matt */
214 1.1 matt and r3, r1, #3 /* extract misalignment */
215 1.1 matt mov r9, r3, lsl #3 /* calculate discard shift */
216 1.1 matt rsb r8, r9, #32 /* calculate insertion shift */
217 1.1 matt #if defined(STRLCPY)
218 1.1 matt add r6, r6, #3 /* compensate for word post-inc */
219 1.1 matt #endif
220 1.1 matt bic r1, r1, #3 /* word align src */
221 1.1 matt ldr r5, [r1], #4 /* load word frm src */
222 1.1 matt mov r4, r5, lslo r9 /* discard lo bytes from src */
223 1.1 matt tst r4, #BYTE0 /* does byte 0 contain a NUL? */
224 1.1 matt #if defined(STRNCPY)
225 1.1 matt beq .Lend_of_string /* yes, zero fill rest of string */
226 1.1 matt #else
227 1.1 matt moveq r3, r9 /* yes, set offset */
228 1.1 matt beq .Lincongruent_end_of_string /* yes, deal with the last bytes */
229 1.1 matt #endif
230 1.1 matt /*
231 1.1 matt * To make our test for NULs below do not generate false positives,
232 1.1 matt * fill the bytes in the word we don't want to match with all 1s.
233 1.1 matt */
234 1.1 matt mvn r3, #0 /* create a mask */
235 1.2 matt mov r3, r3, lslo r8 /* zero out bytes being kept */
236 1.2 matt orr r5, r5, r3 /* merge src and mask */
237 1.1 matt #ifdef _ARM_ARCH_6
238 1.2 matt uqadd8 r3, r5, r7 /* NUL detection magic happens */
239 1.1 matt mvns r3, r3 /* is the complemented result 0? */
240 1.1 matt beq .Lincongruent_mainloop_load /* yes, no NUL encountered! */
241 1.1 matt #ifdef __ARMEL__
242 1.1 matt rev r3, r3 /* CLZ wants BE input */
243 1.1 matt #endif
244 1.1 matt clz r3, r3 /* count leading zeros */
245 1.1 matt #else
246 1.1 matt /*
247 1.1 matt * We already tested for byte 0 above so we don't need to it again.
248 1.1 matt */
249 1.1 matt mov r3, #24 /* assume NUL is in byte 3 */
250 1.1 matt tst r5, #BYTE1 /* did we find a NUL in byte 1? */
251 1.1 matt subeq r3, r3, #8 /* yes, decremnt byte position */
252 1.1 matt tstne r5, #BYTE2 /* no, did we find a NUL in byte 2? */
253 1.1 matt subeq r3, r3, #8 /* yes, decremnt byte position */
254 1.1 matt tstne r5, #BYTE3 /* no, did we find a NUL in byte 3? */
255 1.1 matt bne .Lincongruent_mainloop_load /* no, no NUL encountered! */
256 1.1 matt #endif
257 1.1 matt mov r5, r4 /* discard already dealt with bytes */
258 1.1 matt .Lincongruent_end_of_string:
259 1.1 matt #if defined(STRLCPY)
260 1.1 matt add r1, r1, r3, lsr #3 /* then add offset to NUL */
261 1.1 matt #endif
262 1.1 matt sub r3, r3, r9 /* adjust NUL offset */
263 1.1 matt b .Llast_bytes /* NUL encountered! finish up */
264 1.1 matt
265 1.1 matt #if defined(STRLCPY) || defined(STRNCPY)
266 1.1 matt .Lincongruent_no_more_room:
267 1.1 matt mov r5, r4 /* move data to be stored to r5 */
268 1.1 matt b .Lno_more_room /* fill remaining space */
269 1.1 matt #endif /* STRLCPY || STRNCPY */
270 1.1 matt
271 1.1 matt /*
272 1.1 matt * At this point both dst and src are word aligned and r4 contains
273 1.1 matt * partial contents from src.
274 1.1 matt */
275 1.1 matt .Lincongruent_mainloop:
276 1.1 matt orr r4, r4, r5, lshi r8 /* put new src data into dst word */
277 1.1 matt #if defined(STRLCPY) || defined(STRNCPY)
278 1.1 matt subs r2, r2, #4 /* subtract 4 from count */
279 1.1 matt bmi .Lincongruent_no_more_room /* count < 0? dst will be full */
280 1.1 matt #endif
281 1.1 matt str r4, [r0], #4 /* store word in dst */
282 1.1 matt #if defined(STRLCPY)
283 1.1 matt beq .Lno_more_room /* space left is 0? stop copy */
284 1.1 matt #endif
285 1.1 matt #if defined(STRNCPY)
286 1.1 matt beq .Ldst_full_word_aligned /* space left is 0? stop copy */
287 1.1 matt #endif
288 1.1 matt mov r4, r5, lslo r9 /* move rest of src into dst word */
289 1.1 matt .Lincongruent_mainloop_load:
290 1.1 matt ldr r5, [r1], #4 /* read src */
291 1.1 matt #ifdef _ARM_ARCH_6
292 1.1 matt uqadd8 r3, r5, r7 /* magic happens here */
293 1.1 matt mvns r3, r3 /* is the complemented result 0? */
294 1.1 matt beq .Lincongruent_mainloop /* yes, no NUL encountered! */
295 1.1 matt /*
296 1.1 matt * fall into this since we encountered a NULL. At this point we have
297 1.1 matt * from 1-5 bytes (excluding trailing NUL) to write.
298 1.1 matt */
299 1.1 matt #ifdef __ARMEL__
300 1.1 matt rev r3, r3 /* CLZ works on BE data */
301 1.1 matt #endif
302 1.1 matt clz r3, r3 /* count leading zeroes */
303 1.1 matt #else
304 1.1 matt tst r5, #BYTE0 /* does byte 0 contain a NUL? */
305 1.1 matt tstne r5, #BYTE1 /* no, does byte 1 contain a NUL? */
306 1.1 matt tstne r5, #BYTE2 /* no, does byte 2 contain a NUL? */
307 1.1 matt tstne r5, #BYTE3 /* no, does byte 3 contain a NUL? */
308 1.1 matt bne .Lincongruent_mainloop /* no, no NUL encountered! */
309 1.1 matt /*
310 1.1 matt * fall into this since we encountered a NULL. At this point we have
311 1.1 matt * from 1-5 bytes (excluding trailing NUL) to write.
312 1.1 matt */
313 1.1 matt mov r3, #0 /* assume a NUL is in byte 0 */
314 1.1 matt tst r5, #BYTE0 /* is there a NUL in byte 0? */
315 1.1 matt beq 1f /* yes, found a NUL! */
316 1.1 matt mov r3, #8 /* assume a NUL is in byte 1 */
317 1.1 matt tst r5, #BYTE1 /* is there a NUL in byte 0? */
318 1.1 matt beq 1f /* yes, found a NUL! */
319 1.1 matt tst r5, #BYTE2 /* is there a NUL in byte 2? */
320 1.1 matt moveq r3, #16 /* yes, mark its position */
321 1.1 matt movne r3, #24 /* no, it must be in byte 3 */
322 1.1 matt 1:
323 1.1 matt #endif
324 1.1 matt orr r4, r4, r5, lshi r8 /* merge new and old src words */
325 1.1 matt #if defined(STRLCPY)
326 1.1 matt add r1, r1, r3, lsr #3 /* adjust src to point to NUL */
327 1.1 matt #endif
328 1.1 matt add r3, r3, r8 /* add remainder bytes worth */
329 1.1 matt cmp r3, #32 /* do we have at least one word to write? */
330 1.1 matt movlt r5, r4 /* no, move source bytes to expected reg */
331 1.1 matt blt .Llast_bytes /* no, deal with them */
332 1.1 matt #if defined(STRLCPY)
333 1.1 matt subs r2, r2, #4 /* subtract 4 from count */
334 1.1 matt bpl 1f /* we have space for at least 4 */
335 1.1 matt /*
336 1.1 matt * Since the space just went minus, we don't have enough room to
337 1.1 matt * write all 4 bytes. In fact, the most we can write is 3 so just
338 1.1 matt * just lie and say we have 3 bytes to write and discard the rest.
339 1.1 matt */
340 1.1 matt add r2, r2, #4 /* add 4 back */
341 1.1 matt mov r3, #24 /* say we have 3 bytes */
342 1.1 matt mov r5, r4 /* discard the bytes we can't store */
343 1.1 matt b .Llast_bytes /* and treat this as our last word */
344 1.1 matt 1:
345 1.1 matt #elif defined(STRNCPY)
346 1.1 matt subs r2, r2, #4 /* subtract 4 from count */
347 1.1 matt bmi .Lincongruent_no_more_room /* count < 0? dst will be full */
348 1.1 matt #endif
349 1.1 matt str r4, [r0], #4 /* store dst word */
350 1.1 matt #if defined(STRNCPY)
351 1.1 matt beq .Ldst_full_word_aligned /* space left is 0? stop copy */
352 1.1 matt #endif
353 1.1 matt #if defined(STRLCPY)
354 1.1 matt bne 1f /* we still have space remaining */
355 1.1 matt strb r2, [r0] /* write final NUL */
356 1.1 matt b .Lend_of_string /* we are done */
357 1.1 matt 1:
358 1.1 matt #endif
359 1.1 matt /*
360 1.1 matt * Subtract the 32 bits just written from the number of bits left
361 1.1 matt * to write. If 0 bits are left and not doing strncpy, just write
362 1.1 matt * the trailing NUL and be done.
363 1.1 matt */
364 1.1 matt subs r3, r3, #32 /* we wrote one word */
365 1.1 matt #if !defined(STRNCPY)
366 1.1 matt bne 1f /* no more data? */
367 1.1 matt strb r3, [r0] /* write final NUL */
368 1.1 matt b .Lend_of_string /* we are done */
369 1.1 matt 1:
370 1.1 matt #endif
371 1.1 matt /*
372 1.1 matt * At this point after writing 4 bytes, we have 0 or 1 bytes left to
373 1.1 matt * write (excluding the trailing NUL).
374 1.1 matt */
375 1.1 matt mov r5, r5, lslo r9 /* get remainder of src */
376 1.1 matt
377 1.1 matt /* fall into .Llast_bytes */
378 1.1 matt
379 1.1 matt #if !defined(STRLCPY)
380 1.1 matt .Lcongruent_last_bytes:
381 1.1 matt #endif
382 1.1 matt .Llast_bytes:
383 1.1 matt /*
384 1.1 matt * r5 contains the last word and is in host byte order.
385 1.1 matt * r3 contains number of bits left to copy (0..31).
386 1.1 matt * r1 should point to the NUL + 4.
387 1.1 matt */
388 1.1 matt bics ip, r3, #7 /* truncate bits, is result 0? */
389 1.1 matt #if !defined(STRNCPY)
390 1.1 matt bne 1f /* no, have to write some bytes */
391 1.1 matt strb ip, [r0] /* yes, write trailing NUL */
392 1.1 matt b .Lend_of_string /* yes, and we are the end */
393 1.1 matt 1:
394 1.1 matt #endif
395 1.1 matt #if defined(STRLCPY) || defined(STRNCPY)
396 1.1 matt cmp r2, ip, lsr #3 /* is there enough room? */
397 1.1 matt movlt ip, r2, lsl #3 /* no, only fill remaining space */
398 1.1 matt #endif
399 1.1 matt mvn r3, #0 /* create a mask */
400 1.1 matt mov r3, r3, lshi ip /* clear leading bytes */
401 1.1 matt bic r5, r5, r3 /* clear trailing bytes */
402 1.1 matt #if defined(STRNCPY)
403 1.1 matt cmp r2, #4 /* room for 4 bytes? */
404 1.1 matt movge ip, #32 /* yes, we will write 4 bytes */
405 1.1 matt bge 2f /* yes, and go do it */
406 1.1 matt mvn r3, #0 /* create a mask (again) */
407 1.1 matt mov ip, r2, lsl #3 /* remaining space bytes -> bits */
408 1.1 matt mov r3, r3, lshi ip /* clear remaining bytes */
409 1.1 matt #elif defined(STRLCPY)
410 1.1 matt cmp r2, #3 /* do we have room for 3 bytes & NUL? */
411 1.1 matt bge 2f /* yes, just clear out dst */
412 1.1 matt mov r3, r3, lshi #8 /* mask out trailing NUL */
413 1.1 matt #else
414 1.1 matt cmp ip, #24 /* are we writing 3 bytes & a NUL? */
415 1.1 matt bge 2f /* yes, just overwrite dst */
416 1.1 matt mov r3, r3, lshi #8 /* mask out trailing NUL */
417 1.1 matt #endif /* !STRNCPY */
418 1.1 matt ldr r4, [r0] /* fetch dst word */
419 1.1 matt and r4, r4, r3 /* preserve trailing bytes */
420 1.1 matt orr r5, r5, r4 /* merge dst with src */
421 1.1 matt 2: str r5, [r0], #4 /* store last word */
422 1.1 matt #if defined(STRNCPY)
423 1.1 matt subs r2, r2, ip, lsr #3 /* subtract bytes cleared from count */
424 1.1 matt beq .Ldst_full_word_aligned
425 1.1 matt #endif
426 1.1 matt b .Lend_of_string
427 1.1 matt
428 1.1 matt #if defined(STRLCPY) || defined(STRNCPY)
429 1.1 matt .Lno_more_room:
430 1.1 matt #if defined(STRLCPY)
431 1.1 matt cmp r2, #-1 /* tried to write 3 bytes? */
432 1.1 matt blt 1f /* less, partial word write */
433 1.1 matt cmp r2, #0 /* no space left? */
434 1.3 matt strbeq r2, [r0] /* write the final NUL */
435 1.1 matt bicne r5, r5, #BYTE3 /* clear trailing NUL */
436 1.1 matt strne r5, [r0] /* write last word */
437 1.1 matt b .Ldst_full_word_aligned /* the dst buffer is full */
438 1.1 matt 1:
439 1.1 matt #endif /* STRLCPY */
440 1.1 matt add r2, r2, #4 /* restore remaining space */
441 1.1 matt ldr r4, [r0] /* load dst */
442 1.1 matt mvn r3, #0 /* create a mask */
443 1.1 matt mov r2, r2, lsl #3 /* bytes -> bits */
444 1.1 matt mov r3, r3, lshi r2 /* clear leading bytes */
445 1.1 matt bic r5, r5, r3 /* clear trailing bytes from src */
446 1.1 matt #if defined(STRLCPY)
447 1.1 matt mov r3, r3, lshi #8 /* mask out trailing NUL */
448 1.1 matt #endif /* STRLCPY */
449 1.1 matt and r4, r4, r3 /* preserve trailing bytes in dst */
450 1.1 matt orr r4, r4, r5 /* merge src with dst */
451 1.1 matt str r4, [r0], #4 /* write last word */
452 1.1 matt b .Ldst_full_word_aligned
453 1.1 matt #endif /* STRLCPY || STRNCPY */
454 1.1 matt
455 1.1 matt #if defined(STRLCPY)
456 1.1 matt /*
457 1.1 matt * Destination was filled (and NUL terminated).
458 1.1 matt * All that's left is count the number of bytes left in src.
459 1.1 matt */
460 1.1 matt .Ldst_full:
461 1.1 matt 1: tst r1, #3 /* dst word aligned? */
462 1.1 matt beq 2f /* yes, so do it word by word */
463 1.1 matt ldrb r5, [r1], #1 /* load next byte */
464 1.1 matt teq r5, #0 /* is it a NUL? */
465 1.1 matt bne 1b /* no, check alignment */
466 1.1 matt b .Lend_of_string /* and return */
467 1.1 matt 2: add r6, r6, #3 /* compensate for post-inc */
468 1.1 matt .Ldst_full_word_aligned:
469 1.1 matt 3: ldr r5, [r1], #4 /* load word from src */
470 1.1 matt #ifdef _ARM_ARCH_6
471 1.1 matt uqadd8 r5, r5, r7 /* perform NUL magic */
472 1.1 matt mvns r5, r5 /* complement all 0s? */
473 1.1 matt beq 3b /* yes, no NUL so get next word */
474 1.1 matt #else
475 1.1 matt tst r5, #BYTE0 /* does byte 0 contain a NUL? */
476 1.1 matt tstne r5, #BYTE1 /* no, does byte 1 contain a NUL? */
477 1.1 matt tstne r5, #BYTE2 /* no, does byte 2 contain a NUL? */
478 1.1 matt tstne r5, #BYTE3 /* no, does byte 3 contain a NUL? */
479 1.1 matt bne 3b /* no, no NUL encountered! */
480 1.1 matt #endif
481 1.1 matt #ifdef _ARM_ARCH_6
482 1.1 matt #ifdef __ARMEL__
483 1.1 matt rev r5, r5 /* CLZ needs BE data */
484 1.1 matt #endif
485 1.1 matt clz r5, r5 /* count leading zeros */
486 1.1 matt add r1, r1, r5, lsr #3 /* add offset to NUL to src pointer */
487 1.1 matt #else
488 1.1 matt tst r5, #BYTE0 /* is there a NUL in byte 0? */
489 1.1 matt beq 4f /* yes, don't check any further */
490 1.1 matt add r1, r1, #1 /* no, advance src pointer by 1 */
491 1.1 matt tst r5, #BYTE1 /* is there a NUL in byte 1? */
492 1.1 matt beq 4f /* yes, don't check any further */
493 1.1 matt add r1, r1, #1 /* no, advance src pointer by 1 */
494 1.1 matt tst r5, #BYTE2 /* is there a NUL in byte 2? */
495 1.1 matt addne r1, r1, #1 /* no, there must be in byte 3 */
496 1.1 matt 4:
497 1.1 matt #endif /* _ARM_ARCH_6 */
498 1.1 matt .Lend_of_string:
499 1.1 matt sub r0, r1, r6 /* subtract start from finish */
500 1.1 matt pop {r4-r9} /* restore registers */
501 1.1 matt RET
502 1.1 matt #elif defined(STRNCPY)
503 1.1 matt .Lend_of_string:
504 1.1 matt teq r2, #0 /* any bytes left to zero? */
505 1.1 matt beq 3f /* no, just return. */
506 1.1 matt mov r1, #0 /* yes, prepare to zero */
507 1.1 matt cmp r2, #16 /* some, but not a lot? */
508 1.1 matt ble 1f
509 1.1 matt mov r4, lr /* preserve lr */
510 1.1 matt bl PLT_SYM(_C_LABEL(memset)) /* yes, and let memset do it */
511 1.1 matt mov lr, r4 /* restore lr */
512 1.1 matt b 3f /* return */
513 1.1 matt 1: add ip, r0, r2 /* calculate stopping point */
514 1.1 matt 2: strb r1, [r0], #1 /* clear a byte */
515 1.1 matt cmp r0, ip /* done? */
516 1.1 matt blt 2b /* no, clear next byte */
517 1.1 matt 3: mov r0, r6 /* restore dst pointer */
518 1.1 matt pop {r4-r9} /* restore registers */
519 1.1 matt RET
520 1.1 matt .Ldst_full:
521 1.1 matt .Ldst_full_word_aligned:
522 1.1 matt /*
523 1.1 matt * Destination was filled (but not NUL terminated).
524 1.1 matt * All that's left is return the start of dst
525 1.1 matt */
526 1.1 matt mov r0, r6 /* restore dst pointer */
527 1.1 matt pop {r4-r9} /* restore registers */
528 1.1 matt RET
529 1.1 matt #else
530 1.1 matt .Lend_of_string:
531 1.1 matt mov r0, r6 /* restore dst pointer */
532 1.1 matt pop {r4-r9} /* restore registers */
533 1.1 matt RET
534 1.1 matt #endif
535 1.1 matt END(FUNCNAME)
536