Replace FSF snail mail address by URL.
[glibc.git] / sysdeps / alpha / stxcpy.S
blob46f19a98589fef442d7dd205b322a04dcd64831d
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, see
17    <http://www.gnu.org/licenses/>.  */
19 /* Copy a null-terminated string from SRC to DST.
21    This is an internal routine used by strcpy, stpcpy, and strcat.
22    As such, it uses special linkage conventions to make implementation
23    of these public functions more efficient.
25    On input:
26         t9 = return address
27         a0 = DST
28         a1 = SRC
30    On output:
31         t8  = bitmask (with one bit set) indicating the last byte written
32         a0  = unaligned address of the last *word* written
34    Furthermore, v0, a3-a5, t11, and t12 are untouched.
37 /* This is generally scheduled for the EV5, but should still be pretty
38    good for the EV4 too.  */
40 #include <sysdep.h>
42         .set noat
43         .set noreorder
45         .text
47 /* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that
48    doesn't like putting the entry point for a procedure somewhere in the
49    middle of the procedure descriptor.  Work around this by putting the
50    aligned copy in its own procedure descriptor */
52         .ent stxcpy_aligned
53         .align 3
54 stxcpy_aligned:
55         .frame sp, 0, t9
56         .prologue 0
58         /* On entry to this basic block:
59            t0 == the first destination word for masking back in
60            t1 == the first source word.  */
62         /* Create the 1st output word and detect 0's in the 1st input word.  */
63         lda     t2, -1          # e1    : build a mask against false zero
64         mskqh   t2, a1, t2      # e0    :   detection in the src word
65         mskqh   t1, a1, t3      # e0    :
66         ornot   t1, t2, t2      # .. e1 :
67         mskql   t0, a1, t0      # e0    : assemble the first output word
68         cmpbge  zero, t2, t7    # .. e1 : bits set iff null found
69         or      t0, t3, t1      # e0    :
70         bne     t7, $a_eos      # .. e1 :
72         /* On entry to this basic block:
73            t0 == the first destination word for masking back in
74            t1 == a source word not containing a null.  */
76 $a_loop:
77         stq_u   t1, 0(a0)       # e0    :
78         addq    a0, 8, a0       # .. e1 :
79         ldq_u   t1, 0(a1)       # e0    :
80         addq    a1, 8, a1       # .. e1 :
81         cmpbge  zero, t1, t7    # e0 (stall)
82         beq     t7, $a_loop     # .. e1 (zdb)
84         /* Take care of the final (partial) word store.
85            On entry to this basic block we have:
86            t1 == the source word containing the null
87            t7 == the cmpbge mask that found it.  */
88 $a_eos:
89         negq    t7, t6          # e0    : find low bit set
90         and     t7, t6, t8      # e1 (stall)
92         /* For the sake of the cache, don't read a destination word
93            if we're not going to need it.  */
94         and     t8, 0x80, t6    # e0    :
95         bne     t6, 1f          # .. e1 (zdb)
97         /* We're doing a partial word store and so need to combine
98            our source and original destination words.  */
99         ldq_u   t0, 0(a0)       # e0    :
100         subq    t8, 1, t6       # .. e1 :
101         zapnot  t1, t6, t1      # e0    : clear src bytes >= null
102         or      t8, t6, t7      # .. e1 :
103         zap     t0, t7, t0      # e0    : clear dst bytes <= null
104         or      t0, t1, t1      # e1    :
106 1:      stq_u   t1, 0(a0)       # e0    :
107         ret     (t9)            # .. e1 :
109         .end stxcpy_aligned
111         .align 3
112         .ent __stxcpy
113         .globl __stxcpy
114 __stxcpy:
115         .frame sp, 0, t9
116         .prologue 0
118         /* Are source and destination co-aligned?  */
119         xor     a0, a1, t0      # e0    :
120         unop                    #       :
121         and     t0, 7, t0       # e0    :
122         bne     t0, $unaligned  # .. e1 :
124         /* We are co-aligned; take care of a partial first word.  */
125         ldq_u   t1, 0(a1)       # e0    : load first src word
126         and     a0, 7, t0       # .. e1 : take care not to load a word ...
127         addq    a1, 8, a1               # e0    :
128         beq     t0, stxcpy_aligned      # .. e1 : ... if we wont need it
129         ldq_u   t0, 0(a0)       # e0    :
130         br      stxcpy_aligned  # .. e1 :
133 /* The source and destination are not co-aligned.  Align the destination
134    and cope.  We have to be very careful about not reading too much and
135    causing a SEGV.  */
137         .align 3
138 $u_head:
139         /* We know just enough now to be able to assemble the first
140            full source word.  We can still find a zero at the end of it
141            that prevents us from outputting the whole thing.
143            On entry to this basic block:
144            t0 == the first dest word, for masking back in, if needed else 0
145            t1 == the low bits of the first source word
146            t6 == bytemask that is -1 in dest word bytes */
148         ldq_u   t2, 8(a1)       # e0    :
149         addq    a1, 8, a1       # .. e1 :
151         extql   t1, a1, t1      # e0    :
152         extqh   t2, a1, t4      # e0    :
153         mskql   t0, a0, t0      # e0    :
154         or      t1, t4, t1      # .. e1 :
155         mskqh   t1, a0, t1      # e0    :
156         or      t0, t1, t1      # e1    :
158         or      t1, t6, t6      # e0    :
159         cmpbge  zero, t6, t7    # .. e1 :
160         lda     t6, -1          # e0    : for masking just below
161         bne     t7, $u_final    # .. e1 :
163         mskql   t6, a1, t6              # e0    : mask out the bits we have
164         or      t6, t2, t2              # e1    :   already extracted before
165         cmpbge  zero, t2, t7            # e0    :   testing eos
166         bne     t7, $u_late_head_exit   # .. e1 (zdb)
168         /* Finally, we've got all the stupid leading edge cases taken care
169            of and we can set up to enter the main loop.  */
171         stq_u   t1, 0(a0)       # e0    : store first output word
172         addq    a0, 8, a0       # .. e1 :
173         extql   t2, a1, t0      # e0    : position ho-bits of lo word
174         ldq_u   t2, 8(a1)       # .. e1 : read next high-order source word
175         addq    a1, 8, a1       # e0    :
176         cmpbge  zero, t2, t7    # .. e1 :
177         nop                     # e0    :
178         bne     t7, $u_eos      # .. e1 :
180         /* Unaligned copy main loop.  In order to avoid reading too much,
181            the loop is structured to detect zeros in aligned source words.
182            This has, unfortunately, effectively pulled half of a loop
183            iteration out into the head and half into the tail, but it does
184            prevent nastiness from accumulating in the very thing we want
185            to run as fast as possible.
187            On entry to this basic block:
188            t0 == the shifted high-order bits from the previous source word
189            t2 == the unshifted current source word
191            We further know that t2 does not contain a null terminator.  */
193         .align 3
194 $u_loop:
195         extqh   t2, a1, t1      # e0    : extract high bits for current word
196         addq    a1, 8, a1       # .. e1 :
197         extql   t2, a1, t3      # e0    : extract low bits for next time
198         addq    a0, 8, a0       # .. e1 :
199         or      t0, t1, t1      # e0    : current dst word now complete
200         ldq_u   t2, 0(a1)       # .. e1 : load high word for next time
201         stq_u   t1, -8(a0)      # e0    : save the current word
202         mov     t3, t0          # .. e1 :
203         cmpbge  zero, t2, t7    # e0    : test new word for eos
204         beq     t7, $u_loop     # .. e1 :
206         /* We've found a zero somewhere in the source word we just read.
207            If it resides in the lower half, we have one (probably partial)
208            word to write out, and if it resides in the upper half, we
209            have one full and one partial word left to write out.
211            On entry to this basic block:
212            t0 == the shifted high-order bits from the previous source word
213            t2 == the unshifted current source word.  */
214 $u_eos:
215         extqh   t2, a1, t1      # e0    :
216         or      t0, t1, t1      # e1    : first (partial) source word complete
218         cmpbge  zero, t1, t7    # e0    : is the null in this first bit?
219         bne     t7, $u_final    # .. e1 (zdb)
221 $u_late_head_exit:
222         stq_u   t1, 0(a0)       # e0    : the null was in the high-order bits
223         addq    a0, 8, a0       # .. e1 :
224         extql   t2, a1, t1      # e0    :
225         cmpbge  zero, t1, t7    # .. e1 :
227         /* Take care of a final (probably partial) result word.
228            On entry to this basic block:
229            t1 == assembled source word
230            t7 == cmpbge mask that found the null.  */
231 $u_final:
232         negq    t7, t6          # e0    : isolate low bit set
233         and     t6, t7, t8      # e1    :
235         and     t8, 0x80, t6    # e0    : avoid dest word load if we can
236         bne     t6, 1f          # .. e1 (zdb)
238         ldq_u   t0, 0(a0)       # e0    :
239         subq    t8, 1, t6       # .. e1 :
240         or      t6, t8, t7      # e0    :
241         zapnot  t1, t6, t1      # .. e1 : kill source bytes >= null
242         zap     t0, t7, t0      # e0    : kill dest bytes <= null
243         or      t0, t1, t1      # e1    :
245 1:      stq_u   t1, 0(a0)       # e0    :
246         ret     (t9)            # .. e1 :
248         /* Unaligned copy entry point.  */
249         .align 3
250 $unaligned:
252         ldq_u   t1, 0(a1)       # e0    : load first source word
254         and     a0, 7, t4       # .. e1 : find dest misalignment
255         and     a1, 7, t5       # e0    : find src misalignment
257         /* Conditionally load the first destination word and a bytemask
258            with 0xff indicating that the destination byte is sacrosanct.  */
260         mov     zero, t0        # .. e1 :
261         mov     zero, t6        # e0    :
262         beq     t4, 1f          # .. e1 :
263         ldq_u   t0, 0(a0)       # e0    :
264         lda     t6, -1          # .. e1 :
265         mskql   t6, a0, t6      # e0    :
267         subq    a1, t4, a1      # .. e1 : sub dest misalignment from src addr
269         /* If source misalignment is larger than dest misalignment, we need
270            extra startup checks to avoid SEGV.  */
272         cmplt   t4, t5, t8      # e0    :
273         beq     t8, $u_head     # .. e1 (zdb)
275         lda     t2, -1          # e1    : mask out leading garbage in source
276         mskqh   t2, t5, t2      # e0    :
277         nop                     # e0    :
278         ornot   t1, t2, t3      # .. e1 :
279         cmpbge  zero, t3, t7    # e0    : is there a zero?
280         beq     t7, $u_head     # .. e1 (zdb)
282         /* At this point we've found a zero in the first partial word of
283            the source.  We need to isolate the valid source data and mask
284            it into the original destination data.  (Incidentally, we know
285            that we'll need at least one byte of that original dest word.) */
287         ldq_u   t0, 0(a0)       # e0    :
289         negq    t7, t6          # .. e1 : build bitmask of bytes <= zero
290         and     t6, t7, t8      # e0    :
291         and     a1, 7, t5       # .. e1 :
292         subq    t8, 1, t6       # e0    :
293         or      t6, t8, t7      # e1    :
294         srl     t8, t5, t8      # e0    : adjust final null return value
296         zapnot  t2, t7, t2      # .. e1 : prepare source word; mirror changes
297         and     t1, t2, t1      # e1    : to source validity mask
298         extql   t2, a1, t2      # .. e0 :
299         extql   t1, a1, t1      # e0    :
301         andnot  t0, t2, t0      # .. e1 : zero place for source to reside
302         or      t0, t1, t1      # e1    : and put it there
303         stq_u   t1, 0(a0)       # .. e0 :
304         ret     (t9)
306         .end __stxcpy