update from main archive 961016
[kopensolaris-gnu/glibc.git] / sysdeps / i386 / i586 / strlen.S
1 /* strlen -- Compute length og NUL terminated string.
2 Highly optimized version for ix86, x>=5.
3 Copyright (C) 1995, 1996 Free Software Foundation, Inc.
4 This file is part of the GNU C Library.
5 Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
6
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public
18 License along with the GNU C Library; see the file COPYING.LIB.  If
19 not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 #include <sysdep.h>
23
24 /* This version is especially optimized for the i586 (and following?)
25    processors.  This is mainly done by using the two pipelines.  The
26    version optimized for i486 is weak in this aspect because to get
27    as much parallelism we have to executs some *more* instructions.
28
29    The code below is structured to reflect the pairing of the instructions
30    as *I think* it is.  I have no processor data book to verify this.
31    If you find something you think is incorrect let me know.  */
32
33
34 /* The magic value which is used throughout in the whole code.  */
35 #define magic 0xfefefeff
36
37 /*
38    INPUT PARAMETERS:
39    str          (sp + 4)
40 */
41
42         .text
43 ENTRY(strlen)
44         movl 4(%esp), %eax      /* get string pointer */
45         movl $3, %edx           /* load mask (= 3) */
46
47         andl %eax, %edx         /* separate last two bits of address */
48
49         jz L1                   /* aligned => start loop */
50         jp L0                   /* exactly two bits set */
51
52         cmpb %dh, (%eax)        /* is byte NUL? */
53         je L2                   /* yes => return */
54
55         incl %eax               /* increment pointer */
56         cmpb %dh, (%eax)        /* is byte NUL? */
57
58         je L2                   /* yes => return */
59
60         incl %eax               /* increment pointer */
61         xorl $2, %edx
62
63         jz L1
64
65 L0:     cmpb %dh, (%eax)        /* is byte NUL? */
66         je L2                   /* yes => return */
67
68         incl %eax               /* increment pointer */
69         xorl %edx, %edx         /* We need %edx == 0 for later */
70
71       /* We exit the loop if adding MAGIC_BITS to LONGWORD fails to
72          change any of the hole bits of LONGWORD.
73
74          1) Is this safe?  Will it catch all the zero bytes?
75          Suppose there is a byte with all zeros.  Any carry bits
76          propagating from its left will fall into the hole at its
77          least significant bit and stop.  Since there will be no
78          carry from its most significant bit, the LSB of the
79          byte to the left will be unchanged, and the zero will be
80          detected.
81
82          2) Is this worthwhile?  Will it ignore everything except
83          zero bytes?  Suppose every byte of LONGWORD has a bit set
84          somewhere.  There will be a carry into bit 8.  If bit 8
85          is set, this will carry into bit 16.  If bit 8 is clear,
86          one of bits 9-15 must be set, so there will be a carry
87          into bit 16.  Similarly, there will be a carry into bit
88          24.  If one of bits 24-31 is set, there will be a carry
89          into bit 32 (=carry flag), so all of the hole bits will
90          be changed.
91
92          Note: %edx == 0 in any case here.  */
93
94 L1:
95         movl (%eax), %ecx       /* get word (= 4 bytes) in question */
96         addl $4, %eax           /* adjust pointer for *next* word */
97
98         subl %ecx, %edx         /* first step to negate word */
99         addl $magic, %ecx       /* add magic word */
100
101         decl %edx               /* complete negation of word */
102         jnc L3                  /* previous addl caused overflow? */
103
104         xorl %ecx, %edx         /* (word+magic)^word */
105
106         andl $~magic, %edx      /* any of the carry flags set? */
107
108         jne L3                  /* yes => determine byte */
109
110
111         movl (%eax), %ecx       /* get word (= 4 bytes) in question */
112         addl $4, %eax           /* adjust pointer for *next* word */
113
114         subl %ecx, %edx         /* first step to negate word */
115         addl $magic, %ecx       /* add magic word */
116
117         decl %edx               /* complete negation of word */
118         jnc L3                  /* previous addl caused overflow? */
119
120         xorl %ecx, %edx         /* (word+magic)^word */
121
122         andl $~magic, %edx      /* any of the carry flags set? */
123
124         jne L3                  /* yes => determine byte */
125
126
127         movl (%eax), %ecx       /* get word (= 4 bytes) in question */
128         addl $4, %eax           /* adjust pointer for *next* word */
129
130         subl %ecx, %edx         /* first step to negate word */
131         addl $magic, %ecx       /* add magic word */
132
133         decl %edx               /* complete negation of word */
134         jnc L3                  /* previous addl caused overflow? */
135
136         xorl %ecx, %edx         /* (word+magic)^word */
137
138         andl $~magic, %edx      /* any of the carry flags set? */
139
140         jne L3                  /* yes => determine byte */
141
142
143         movl (%eax), %ecx       /* get word (= 4 bytes) in question */
144         addl $4, %eax           /* adjust pointer for *next* word */
145
146         subl %ecx, %edx         /* first step to negate word */
147         addl $magic, %ecx       /* add magic word */
148
149         decl %edx               /* complete negation of word */
150         jnc L3                  /* previous addl caused overflow? */
151
152         xorl %ecx, %edx         /* (word+magic)^word */
153
154         andl $~magic, %edx      /* any of the carry flags set? */
155
156         je L1                   /* no => start loop again */
157
158
159 L3:     subl $4, %eax           /* correct too early pointer increment */
160         subl $magic, %ecx
161
162         cmpb $0, %cl            /* lowest byte NUL? */
163         jz L2                   /* yes => return */
164
165         inc %eax                /* increment pointer */
166         testb %ch, %ch          /* second byte NUL? */
167
168         jz L2                   /* yes => return */
169
170         shrl $16, %ecx          /* make upper bytes accessible */
171         incl %eax               /* increment pointer */
172
173         cmpb $0, %cl            /* is third byte NUL? */
174         jz L2                   /* yes => return */
175
176         incl %eax               /* increment pointer */
177
178 L2:     subl 4(%esp), %eax      /* now compute the length as difference
179                                    between start and terminating NUL
180                                    character */
181
182         ret
183 PSEUDO_END (strlen)