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