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