Update.
[glibc.git] / sysdeps / alpha / stxcpy.S
blob5ba2d43e54837b637b469dbf72120da18d56f9ce
1 /* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
2    Contributed by Richard Henderson (rth@tamu.edu)
3    This file is part of the GNU C Library.
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
20 /* Copy a null-terminated string from SRC to DST.
22    This is an internal routine used by strcpy, stpcpy, and strcat.
23    As such, it uses special linkage conventions to make implementation
24    of these public functions more efficient.
26    On input:
27         t9 = return address
28         a0 = DST
29         a1 = SRC
31    On output:
32         t8  = bitmask (with one bit set) indicating the last byte written
33         a0  = unaligned address of the last *word* written
35    Furthermore, v0, a3-a5, t11, and t12 are untouched.
38 /* This is generally scheduled for the EV5, but should still be pretty
39    good for the EV4 too.  */
41 #include <sysdep.h>
43         .set noat
44         .set noreorder
46         .text
48 /* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that
49    doesn't like putting the entry point for a procedure somewhere in the
50    middle of the procedure descriptor.  Work around this by putting the
51    aligned copy in its own procedure descriptor */
53         .ent stxcpy_aligned
54         .align 3
55 stxcpy_aligned:
56         .frame sp, 0, t9
57         .prologue 0
59         /* On entry to this basic block:
60            t0 == the first destination word for masking back in
61            t1 == the first source word.  */
63         /* Create the 1st output word and detect 0's in the 1st input word.  */
64         lda     t2, -1          # e1    : build a mask against false zero
65         mskqh   t2, a1, t2      # e0    :   detection in the src word
66         mskqh   t1, a1, t3      # e0    :
67         ornot   t1, t2, t2      # .. e1 :
68         mskql   t0, a1, t0      # e0    : assemble the first output word
69         cmpbge  zero, t2, t7    # .. e1 : bits set iff null found
70         or      t0, t3, t1      # e0    :
71         bne     t7, $a_eos      # .. e1 :
73         /* On entry to this basic block:
74            t0 == the first destination word for masking back in
75            t1 == a source word not containing a null.  */
77 $a_loop:
78         stq_u   t1, 0(a0)       # e0    :
79         addq    a0, 8, a0       # .. e1 :
80         ldq_u   t1, 0(a1)       # e0    :
81         addq    a1, 8, a1       # .. e1 :
82         cmpbge  zero, t1, t7    # e0 (stall)
83         beq     t7, $a_loop     # .. e1 (zdb)
85         /* Take care of the final (partial) word store.
86            On entry to this basic block we have:
87            t1 == the source word containing the null
88            t7 == the cmpbge mask that found it.  */
89 $a_eos:
90         negq    t7, t6          # e0    : find low bit set
91         and     t7, t6, t8      # e1 (stall)
93         /* For the sake of the cache, don't read a destination word
94            if we're not going to need it.  */
95         and     t8, 0x80, t6    # e0    :
96         bne     t6, 1f          # .. e1 (zdb)
98         /* We're doing a partial word store and so need to combine
99            our source and original destination words.  */
100         ldq_u   t0, 0(a0)       # e0    :
101         subq    t8, 1, t6       # .. e1 :
102         zapnot  t1, t6, t1      # e0    : clear src bytes >= null
103         or      t8, t6, t7      # .. e1 :
104         zap     t0, t7, t0      # e0    : clear dst bytes <= null
105         or      t0, t1, t1      # e1    :
107 1:      stq_u   t1, 0(a0)       # e0    :
108         ret     (t9)            # .. e1 :
110         .end stxcpy_aligned
112         .align 3
113         .ent __stxcpy
114         .globl __stxcpy
115 __stxcpy:
116         .frame sp, 0, t9
117         .prologue 0
119         /* Are source and destination co-aligned?  */
120         xor     a0, a1, t0      # e0    :
121         unop                    #       :
122         and     t0, 7, t0       # e0    :
123         bne     t0, $unaligned  # .. e1 :
125         /* We are co-aligned; take care of a partial first word.  */
126         ldq_u   t1, 0(a1)       # e0    : load first src word
127         and     a0, 7, t0       # .. e1 : take care not to load a word ...
128         addq    a1, 8, a1               # e0    :
129         beq     t0, stxcpy_aligned      # .. e1 : ... if we wont need it
130         ldq_u   t0, 0(a0)       # e0    :
131         br      stxcpy_aligned  # .. e1 :
134 /* The source and destination are not co-aligned.  Align the destination
135    and cope.  We have to be very careful about not reading too much and
136    causing a SEGV.  */
138         .align 3
139 $u_head:
140         /* We know just enough now to be able to assemble the first
141            full source word.  We can still find a zero at the end of it
142            that prevents us from outputting the whole thing.
144            On entry to this basic block:
145            t0 == the first dest word, for masking back in, if needed else 0
146            t1 == the low bits of the first source word
147            t6 == bytemask that is -1 in dest word bytes */
149         ldq_u   t2, 8(a1)       # e0    :
150         addq    a1, 8, a1       # .. e1 :
152         extql   t1, a1, t1      # e0    :
153         extqh   t2, a1, t4      # e0    :
154         mskql   t0, a0, t0      # e0    :
155         or      t1, t4, t1      # .. e1 :
156         mskqh   t1, a0, t1      # e0    :
157         or      t0, t1, t1      # e1    :
159         or      t1, t6, t6      # e0    :
160         cmpbge  zero, t6, t7    # .. e1 :
161         lda     t6, -1          # e0    : for masking just below
162         bne     t7, $u_final    # .. e1 :
164         mskql   t6, a1, t6              # e0    : mask out the bits we have
165         or      t6, t2, t2              # e1    :   already extracted before
166         cmpbge  zero, t2, t7            # e0    :   testing eos
167         bne     t7, $u_late_head_exit   # .. e1 (zdb)
169         /* Finally, we've got all the stupid leading edge cases taken care
170            of and we can set up to enter the main loop.  */
172         stq_u   t1, 0(a0)       # e0    : store first output word
173         addq    a0, 8, a0       # .. e1 :
174         extql   t2, a1, t0      # e0    : position ho-bits of lo word
175         ldq_u   t2, 8(a1)       # .. e1 : read next high-order source word
176         addq    a1, 8, a1       # e0    :
177         cmpbge  zero, t2, t7    # .. e1 :
178         nop                     # e0    :
179         bne     t7, $u_eos      # .. e1 :
181         /* Unaligned copy main loop.  In order to avoid reading too much,
182            the loop is structured to detect zeros in aligned source words.
183            This has, unfortunately, effectively pulled half of a loop
184            iteration out into the head and half into the tail, but it does
185            prevent nastiness from accumulating in the very thing we want
186            to run as fast as possible.
188            On entry to this basic block:
189            t0 == the shifted high-order bits from the previous source word
190            t2 == the unshifted current source word
192            We further know that t2 does not contain a null terminator.  */
194         .align 3
195 $u_loop:
196         extqh   t2, a1, t1      # e0    : extract high bits for current word
197         addq    a1, 8, a1       # .. e1 :
198         extql   t2, a1, t3      # e0    : extract low bits for next time
199         addq    a0, 8, a0       # .. e1 :
200         or      t0, t1, t1      # e0    : current dst word now complete
201         ldq_u   t2, 0(a1)       # .. e1 : load high word for next time
202         stq_u   t1, -8(a0)      # e0    : save the current word
203         mov     t3, t0          # .. e1 :
204         cmpbge  zero, t2, t7    # e0    : test new word for eos
205         beq     t7, $u_loop     # .. e1 :
207         /* We've found a zero somewhere in the source word we just read.
208            If it resides in the lower half, we have one (probably partial)
209            word to write out, and if it resides in the upper half, we
210            have one full and one partial word left to write out.
212            On entry to this basic block:
213            t0 == the shifted high-order bits from the previous source word
214            t2 == the unshifted current source word.  */
215 $u_eos:
216         extqh   t2, a1, t1      # e0    :
217         or      t0, t1, t1      # e1    : first (partial) source word complete
219         cmpbge  zero, t1, t7    # e0    : is the null in this first bit?
220         bne     t7, $u_final    # .. e1 (zdb)
222 $u_late_head_exit:
223         stq_u   t1, 0(a0)       # e0    : the null was in the high-order bits
224         addq    a0, 8, a0       # .. e1 :
225         extql   t2, a1, t1      # e0    :
226         cmpbge  zero, t1, t7    # .. e1 :
228         /* Take care of a final (probably partial) result word.
229            On entry to this basic block:
230            t1 == assembled source word
231            t7 == cmpbge mask that found the null.  */
232 $u_final:
233         negq    t7, t6          # e0    : isolate low bit set
234         and     t6, t7, t8      # e1    :
236         and     t8, 0x80, t6    # e0    : avoid dest word load if we can
237         bne     t6, 1f          # .. e1 (zdb)
239         ldq_u   t0, 0(a0)       # e0    :
240         subq    t8, 1, t6       # .. e1 :
241         or      t6, t8, t7      # e0    :
242         zapnot  t1, t6, t1      # .. e1 : kill source bytes >= null
243         zap     t0, t7, t0      # e0    : kill dest bytes <= null
244         or      t0, t1, t1      # e1    :
246 1:      stq_u   t1, 0(a0)       # e0    :
247         ret     (t9)            # .. e1 :
249         /* Unaligned copy entry point.  */
250         .align 3
251 $unaligned:
253         ldq_u   t1, 0(a1)       # e0    : load first source word
255         and     a0, 7, t4       # .. e1 : find dest misalignment
256         and     a1, 7, t5       # e0    : find src misalignment
258         /* Conditionally load the first destination word and a bytemask
259            with 0xff indicating that the destination byte is sacrosanct.  */
261         mov     zero, t0        # .. e1 :
262         mov     zero, t6        # e0    :
263         beq     t4, 1f          # .. e1 :
264         ldq_u   t0, 0(a0)       # e0    :
265         lda     t6, -1          # .. e1 :
266         mskql   t6, a0, t6      # e0    :
268         subq    a1, t4, a1      # .. e1 : sub dest misalignment from src addr
270         /* If source misalignment is larger than dest misalignment, we need
271            extra startup checks to avoid SEGV.  */
273         cmplt   t4, t5, t8      # e0    :
274         beq     t8, $u_head     # .. e1 (zdb)
276         lda     t2, -1          # e1    : mask out leading garbage in source
277         mskqh   t2, t5, t2      # e0    :
278         nop                     # e0    :
279         ornot   t1, t2, t3      # .. e1 :
280         cmpbge  zero, t3, t7    # e0    : is there a zero?
281         beq     t7, $u_head     # .. e1 (zdb)
283         /* At this point we've found a zero in the first partial word of
284            the source.  We need to isolate the valid source data and mask
285            it into the original destination data.  (Incidentally, we know
286            that we'll need at least one byte of that original dest word.) */
288         ldq_u   t0, 0(a0)       # e0    :
290         negq    t7, t6          # .. e1 : build bitmask of bytes <= zero
291         and     t6, t7, t8      # e0    :
292         and     a1, 7, t5       # .. e1 :
293         subq    t8, 1, t6       # e0    :
294         or      t6, t8, t7      # e1    :
295         srl     t8, t5, t8      # e0    : adjust final null return value
297         zapnot  t2, t7, t2      # .. e1 : prepare source word; mirror changes
298         and     t1, t2, t1      # e1    : to source validity mask
299         extql   t2, a1, t2      # .. e0 :
300         extql   t1, a1, t1      # e0    :
302         andnot  t0, t2, t0      # .. e1 : zero place for source to reside
303         or      t0, t1, t1      # e1    : and put it there
304         stq_u   t1, 0(a0)       # .. e0 :
305         ret     (t9)
307         .end __stxcpy