diff options
Diffstat (limited to 'arch/xtensa/lib/strnlen_user.S')
-rw-r--r-- | arch/xtensa/lib/strnlen_user.S | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/arch/xtensa/lib/strnlen_user.S b/arch/xtensa/lib/strnlen_user.S new file mode 100644 index 000000000000..cdff4d670f3b --- /dev/null +++ b/arch/xtensa/lib/strnlen_user.S | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * arch/xtensa/lib/strnlen_user.S | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General | ||
5 | * Public License. See the file "COPYING" in the main directory of | ||
6 | * this archive for more details. | ||
7 | * | ||
8 | * Returns strnlen, including trailing zero terminator. | ||
9 | * Zero indicates error. | ||
10 | * | ||
11 | * Copyright (C) 2002 Tensilica Inc. | ||
12 | */ | ||
13 | |||
14 | #include <xtensa/coreasm.h> | ||
15 | |||
16 | /* Load or store instructions that may cause exceptions use the EX macro. */ | ||
17 | |||
18 | #define EX(insn,reg1,reg2,offset,handler) \ | ||
19 | 9: insn reg1, reg2, offset; \ | ||
20 | .section __ex_table, "a"; \ | ||
21 | .word 9b, handler; \ | ||
22 | .previous | ||
23 | |||
24 | /* | ||
25 | * size_t __strnlen_user(const char *s, size_t len) | ||
26 | */ | ||
27 | .text | ||
28 | .begin literal | ||
29 | .align 4 | ||
30 | .Lmask0: | ||
31 | .byte 0xff, 0x00, 0x00, 0x00 | ||
32 | .Lmask1: | ||
33 | .byte 0x00, 0xff, 0x00, 0x00 | ||
34 | .Lmask2: | ||
35 | .byte 0x00, 0x00, 0xff, 0x00 | ||
36 | .Lmask3: | ||
37 | .byte 0x00, 0x00, 0x00, 0xff | ||
38 | .end literal | ||
39 | |||
40 | # Register use: | ||
41 | # a2/ src | ||
42 | # a3/ len | ||
43 | # a4/ tmp | ||
44 | # a5/ mask0 | ||
45 | # a6/ mask1 | ||
46 | # a7/ mask2 | ||
47 | # a8/ mask3 | ||
48 | # a9/ tmp | ||
49 | # a10/ tmp | ||
50 | |||
51 | .align 4 | ||
52 | .global __strnlen_user | ||
53 | .type __strnlen_user,@function | ||
54 | __strnlen_user: | ||
55 | entry sp, 16 # minimal stack frame | ||
56 | # a2/ s, a3/ len | ||
57 | addi a4, a2, -4 # because we overincrement at the end; | ||
58 | # we compensate with load offsets of 4 | ||
59 | l32r a5, .Lmask0 # mask for byte 0 | ||
60 | l32r a6, .Lmask1 # mask for byte 1 | ||
61 | l32r a7, .Lmask2 # mask for byte 2 | ||
62 | l32r a8, .Lmask3 # mask for byte 3 | ||
63 | bbsi.l a2, 0, .L1mod2 # if only 8-bit aligned | ||
64 | bbsi.l a2, 1, .L2mod4 # if only 16-bit aligned | ||
65 | |||
66 | /* | ||
67 | * String is word-aligned. | ||
68 | */ | ||
69 | .Laligned: | ||
70 | srli a10, a3, 2 # number of loop iterations with 4B per loop | ||
71 | #if XCHAL_HAVE_LOOPS | ||
72 | loopnez a10, .Ldone | ||
73 | #else | ||
74 | beqz a10, .Ldone | ||
75 | slli a10, a10, 2 | ||
76 | add a10, a10, a4 # a10 = end of last 4B chunk | ||
77 | #endif /* XCHAL_HAVE_LOOPS */ | ||
78 | .Loop: | ||
79 | EX(l32i, a9, a4, 4, lenfixup) # get next word of string | ||
80 | addi a4, a4, 4 # advance string pointer | ||
81 | bnone a9, a5, .Lz0 # if byte 0 is zero | ||
82 | bnone a9, a6, .Lz1 # if byte 1 is zero | ||
83 | bnone a9, a7, .Lz2 # if byte 2 is zero | ||
84 | bnone a9, a8, .Lz3 # if byte 3 is zero | ||
85 | #if !XCHAL_HAVE_LOOPS | ||
86 | blt a4, a10, .Loop | ||
87 | #endif | ||
88 | |||
89 | .Ldone: | ||
90 | EX(l32i, a9, a4, 4, lenfixup) # load 4 bytes for remaining checks | ||
91 | |||
92 | bbci.l a3, 1, .L100 | ||
93 | # check two more bytes (bytes 0, 1 of word) | ||
94 | addi a4, a4, 2 # advance string pointer | ||
95 | bnone a9, a5, .Lz0 # if byte 0 is zero | ||
96 | bnone a9, a6, .Lz1 # if byte 1 is zero | ||
97 | .L100: | ||
98 | bbci.l a3, 0, .L101 | ||
99 | # check one more byte (byte 2 of word) | ||
100 | # Actually, we don't need to check. Zero or nonzero, we'll add one. | ||
101 | # Do not add an extra one for the NULL terminator since we have | ||
102 | # exhausted the original len parameter. | ||
103 | addi a4, a4, 1 # advance string pointer | ||
104 | .L101: | ||
105 | sub a2, a4, a2 # compute length | ||
106 | retw | ||
107 | |||
108 | # NOTE that in several places below, we point to the byte just after | ||
109 | # the zero byte in order to include the NULL terminator in the count. | ||
110 | |||
111 | .Lz3: # byte 3 is zero | ||
112 | addi a4, a4, 3 # point to zero byte | ||
113 | .Lz0: # byte 0 is zero | ||
114 | addi a4, a4, 1 # point just beyond zero byte | ||
115 | sub a2, a4, a2 # subtract to get length | ||
116 | retw | ||
117 | .Lz1: # byte 1 is zero | ||
118 | addi a4, a4, 1+1 # point just beyond zero byte | ||
119 | sub a2, a4, a2 # subtract to get length | ||
120 | retw | ||
121 | .Lz2: # byte 2 is zero | ||
122 | addi a4, a4, 2+1 # point just beyond zero byte | ||
123 | sub a2, a4, a2 # subtract to get length | ||
124 | retw | ||
125 | |||
126 | .L1mod2: # address is odd | ||
127 | EX(l8ui, a9, a4, 4, lenfixup) # get byte 0 | ||
128 | addi a4, a4, 1 # advance string pointer | ||
129 | beqz a9, .Lz3 # if byte 0 is zero | ||
130 | bbci.l a4, 1, .Laligned # if string pointer is now word-aligned | ||
131 | |||
132 | .L2mod4: # address is 2 mod 4 | ||
133 | addi a4, a4, 2 # advance ptr for aligned access | ||
134 | EX(l32i, a9, a4, 0, lenfixup) # get word with first two bytes of string | ||
135 | bnone a9, a7, .Lz2 # if byte 2 (of word, not string) is zero | ||
136 | bany a9, a8, .Laligned # if byte 3 (of word, not string) is nonzero | ||
137 | # byte 3 is zero | ||
138 | addi a4, a4, 3+1 # point just beyond zero byte | ||
139 | sub a2, a4, a2 # subtract to get length | ||
140 | retw | ||
141 | |||
142 | .section .fixup, "ax" | ||
143 | .align 4 | ||
144 | lenfixup: | ||
145 | movi a2, 0 | ||
146 | retw | ||
147 | |||