xref: /xnu-8019.80.24/osfmk/arm/strnlen.s (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
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 _strnlen
33
34#define addr r0
35#define maxl r1
36#define temp r2
37#define mask r3
38#define save ip
39#define word lr
40#define byte lr
41#define indx r0
42
43.macro IfHS_and_WordDoesntContainNUL_SetZ
44#if defined _ARM_ARCH_6
45//  In each word of the string, we check for NUL bytes via a saturating
46//  unsigned subtraction of each byte from 0x1.  The result of this is
47//  non-zero if and only if the corresponding byte in the string is NUL.
48//  Simply using a TST instruction checks all four bytes for NULs in one
49//  go.
50    uqsub8  temp,   mask,   word
51    tsths   temp,           temp
52#else
53//  If we're on armv5, we do not have the uqsub8 instruction, so we need
54//  to use a different test for NUL.  Instead, we compute:
55//
56//      byte - 0x1 & ~byte
57//
58//  and test the high-order bit.  If it is set, then byte is NUL.  Just
59//  as with the other test, this can be applied simultaneously to all
60//  bytes in a word.
61    sub     temp,   word,   mask
62    bic     temp,   temp,   word
63    tsths   temp,           mask, lsl #7
64#endif
65.endm
66
67.text
68.align 3
69.long 0x0           // padding
70.long 0x01010101    // mask for use in finding NULs
71_strnlen:
72//  Establish stack frame, load mask that we will use to find NUL bytes,
73//  and set aside a copy of the pointer to the string.  Subtract 4 from
74//  the maxlen, and jump into a byte-by-byte search if this requires a
75//  borrow, as we cannot use a word-by-word search in that case.
76    push    {r7,lr}
77    mov     r7,     sp
78    ldr     mask,   (_strnlen-4)
79    add		save,   addr,   #4
80    subs    maxl,   maxl,   #4
81    blo     L_bytewiseSearch
82
83//  Load the aligned word that contains the start of the string, then OR
84//  0x01 into any bytes that preceed the start to prevent false positives
85//  when we check for NUL bytes.  Additionally, add the number of unused
86//  bytes to maxlen.
87    and     temp,   addr,   #3
88    bic     addr,   addr,   #3
89    add     maxl,   maxl,   temp
90    lsl     temp,   temp,   #3
91    ldr     word,  [addr],  #4
92    rsb     temp,   temp,   #32
93    orr     word,   word,   mask, lsr temp
94
95    subs    maxl,   maxl,   #4
96    IfHS_and_WordDoesntContainNUL_SetZ
97    bne     1f
98
99.align 4
1000:  ldr     word,  [addr],  #4
101    subs    maxl,   maxl,   #4
102    IfHS_and_WordDoesntContainNUL_SetZ
103    beq     0b
104
105.align 4
106//  Either the last word that we loaded contained a NUL, or we will
107//  exceed maxlen before we finish the next word in the string.  Determine
108//  which case we are in by repeating the check for NUL, and branch if
109//  there was not a NUL byte.  Padding ensures that we don't have two
110//  branches in a single 16-byte fetch group, as this interferes with
111//  branch prediction on Swift.
1121:  tst     temp,           temp
113    beq     L_bytewiseSearch
114
115//  The last word that we loaded contained a NUL.  Subtracting the saved
116//  pointer from the current pointer gives us the number of bytes from
117//  the start of the string to the word containing the NUL.
118    sub     indx,   addr,   save
119#if defined _ARM_ARCH_6
120//  To that we add the index of the first NUL byte in the word, computed
121//  using REV and CLZ followed by a shift.
122    rev     temp,           temp
123    clz     temp,           temp
124    add     indx,   indx,   temp, lsr #3
125#else
126//  armv5 does not have the REV instruction, so instead we find the
127//  index of the NUL byte in word with a linear search.
128    tst     word,           #0x000000ff
129    addne   indx,           #1
130    tstne   word,           #0x0000ff00
131    addne   indx,           #1
132    tstne   word,           #0x00ff0000
133    addne   indx,           #1
134#endif
135    pop     {r7,pc}
136
137.align 4
138L_bytewiseSearch:
139//  Restore maxlen (the last thing that happened before we branched here
140//  was that we subtracted 4 from maxlen), and adjust the saved string
141//  pointer.  Then we do a simple byte-by-byte search until we either
142//  reach the end of the string or maxlen reaches zero, at which point
143//  the length to return is simply the difference between the current
144//  and saved pointers.
145    adds    maxl,   maxl,   #4
146    sub     save,   save,   #4
147    beq     1f
1480:  ldrb    byte,          [addr]
149    cmp     byte,           #0
150    addhi   addr,           #1
151    subshi  maxl,           #1
152    bhi     0b
1531:  sub     indx,   addr,   save
154    pop     {r7,pc}
155