xref: /xnu-8020.121.3/osfmk/arm/strlen.s (revision fdd8201d7b966f0c3ea610489d29bd841d358941)
1/*
2 * Copyright (c) 2011 Apple, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <arm/arch.h>
30.syntax unified
31.code 32
32.globl _strlen
33
34#define addr r0
35#define word r1
36#define temp r2
37#define mask r3
38#define save ip
39#define indx r0
40
41.macro IfWordDoesntContainNUL_SetZ
42#if defined _ARM_ARCH_6
43//  In each word of the string, we check for NUL bytes via a saturating
44//  unsigned subtraction of each byte from 0x1.  The result of this is
45//  non-zero if and only if the corresponding byte in the string is NUL.
46//  Simply using a TST instruction checks all four bytes for NULs in one
47//  go.
48    uqsub8  temp,   mask,   word
49    tst     temp,           temp
50#else
51//  If we're on armv5, we do not have the uqsub8 instruction, so we need
52//  to use a different test for NUL.  Instead, we compute:
53//
54//      byte - 0x1 & ~byte
55//
56//  and test the high-order bit.  If it is set, then byte is NUL.  Just
57//  as with the other test, this can be applied simultaneously to all
58//  bytes in a word.
59    sub     temp,   word,   mask
60    bic     temp,   temp,   word
61    tst     temp,           mask, lsl #7
62#endif
63.endm
64
65.text
66.align 4
67.long 0x0           // padding
68.long 0x01010101    // mask for use in finding NULs
69_strlen:
70//  Establish stack frame, load mask that we will use to find NUL bytes,
71//  and set aside a copy of the pointer to the string.
72    push    {r7,lr}
73    mov     r7,     sp
74    ldr     mask,   (_strlen-4)
75    add		save,   addr,   #4
76
77//  Load the aligned word that contains the start of the string, then OR
78//  0x01 into any bytes that preceed the start of the string to prevent
79//  false positives when we check for NUL bytes.
80    and     temp,   addr,   #3
81    bic     addr,   addr,   #3
82    lsl     temp,   temp,   #3
83    ldr     word,  [addr],  #4
84    rsb     temp,   temp,   #32
85    orr     word,   word,   mask, lsr temp
86
87//  Check if the string ends in the first word.  If so, don't load any
88//  more of the string; instead jump directly to the cleanup code.
89    IfWordDoesntContainNUL_SetZ
90    bne     1f
91
92.align 4
93//  Load one word of the string on each iteration, and check it for NUL
94//  bytes.  If a NUL is found, fall through into the cleanup code.
950:  ldr     word,  [addr],  #4
96    IfWordDoesntContainNUL_SetZ
97    beq		0b
98
99//  The last word that we loaded contained a NUL.  Subtracting the saved
100//  pointer from the current pointer gives us the number of bytes from
101//  the start of the string to the word containing the NUL.
1021:  sub     indx,   addr,   save
103#if defined _ARM_ARCH_6
104//  To that we add the index of the first NUL byte in the word, computed
105//  using REV and CLZ followed by a shift.
106    rev     temp,           temp
107    clz     temp,           temp
108    add     indx,   indx,   temp, lsr #3
109#else
110//  armv5 does not have the REV instruction, so instead we find the
111//  index of the NUL byte in word with a linear search.
112    tst     word,           #0x000000ff
113    addne   indx,           #1
114    tstne   word,           #0x0000ff00
115    addne   indx,           #1
116    tstne   word,           #0x00ff0000
117    addne   indx,           #1
118#endif
119    pop     {r7,pc}
120