Alpha ev6 stxcpy implementation.
[kopensolaris-gnu/glibc.git] / sysdeps / alpha / alphaev6 / stxcpy.S
1 /* Copyright (C) 2000 Free Software Foundation, Inc.
2    Contributed by Richard Henderson (rth@tamu.edu)
3    EV6 optimized by Rick Gorton <rick.gorton@alpha-processor.com>.
4
5    This file is part of the GNU C Library.
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 not,
19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.  */
21
22 /* Copy a null-terminated string from SRC to DST.
23
24    This is an internal routine used by strcpy, stpcpy, and strcat.
25    As such, it uses special linkage conventions to make implementation
26    of these public functions more efficient.
27
28    On input:
29         t9 = return address
30         a0 = DST
31         a1 = SRC
32
33    On output:
34         t8  = bitmask (with one bit set) indicating the last byte written
35         a0  = unaligned address of the last *word* written
36
37    Furthermore, v0, a3-a5, t11, and t12 are untouched.
38 */
39
40
41 #include <sysdep.h>
42
43         .arch ev6
44         .set noat
45         .set noreorder
46         .text
47
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 */
52
53
54         .ent stxcpy_aligned
55         .align 4
56 stxcpy_aligned:
57         .frame sp, 0, t9
58         .prologue 0
59
60         /* On entry to this basic block:
61            t0 == the first destination word for masking back in
62            t1 == the first source word.  */
63
64         /* Create the 1st output word and detect 0's in the 1st input word.  */
65         lda     t2, -1          # E : build a mask against false zero
66         mskqh   t2, a1, t2      # U :   detection in the src word (stall)
67         mskqh   t1, a1, t3      # U :
68         ornot   t1, t2, t2      # E : (stall)
69
70         mskql   t0, a1, t0      # U : assemble the first output word
71         cmpbge  zero, t2, t8    # E : bits set iff null found
72         or      t0, t3, t1      # E : (stall)
73         bne     t8, $a_eos      # U : (stall)
74
75         /* On entry to this basic block:
76            t0 == the first destination word for masking back in
77            t1 == a source word not containing a null.  */
78         /* Nops here to separate store quads from load quads */
79
80 $a_loop:
81         stq_u   t1, 0(a0)       # L :
82         addq    a0, 8, a0       # E :
83         nop
84         nop
85
86         ldq_u   t1, 0(a1)       # L : Latency=3
87         addq    a1, 8, a1       # E :
88         cmpbge  zero, t1, t8    # E : (3 cycle stall)
89         beq     t8, $a_loop     # U : (stall for t8)
90
91         /* Take care of the final (partial) word store.
92            On entry to this basic block we have:
93            t1 == the source word containing the null
94            t8 == the cmpbge mask that found it.  */
95 $a_eos:
96         negq    t8, t6          # E : find low bit set
97         and     t8, t6, t10     # E : (stall)
98         /* For the sake of the cache, don't read a destination word
99            if we're not going to need it.  */
100         and     t10, 0x80, t6   # E : (stall)
101         bne     t6, 1f          # U : (stall)
102
103         /* We're doing a partial word store and so need to combine
104            our source and original destination words.  */
105         ldq_u   t0, 0(a0)       # L : Latency=3
106         subq    t10, 1, t6      # E :
107         zapnot  t1, t6, t1      # U : clear src bytes >= null (stall)
108         or      t10, t6, t8     # E : (stall)
109
110         zap     t0, t8, t0      # E : clear dst bytes <= null
111         or      t0, t1, t1      # E : (stall)
112         nop
113         nop
114
115 1:      stq_u   t1, 0(a0)       # L :
116         ret     (t9)            # L0 : Latency=3
117         nop
118         nop
119
120         .end stxcpy_aligned
121
122         .align 4
123         .ent __stxcpy
124         .globl __stxcpy
125 __stxcpy:
126         .frame sp, 0, t9
127         .prologue 0
128
129         /* Are source and destination co-aligned?  */
130         xor     a0, a1, t0      # E :
131         unop                    # E :
132         and     t0, 7, t0       # E : (stall)
133         bne     t0, $unaligned  # U : (stall)
134
135         /* We are co-aligned; take care of a partial first word.  */
136         ldq_u   t1, 0(a1)               # L : load first src word
137         and     a0, 7, t0               # E : take care not to load a word ...
138         addq    a1, 8, a1               # E :
139         beq     t0, stxcpy_aligned      # U : ... if we wont need it (stall)
140
141         ldq_u   t0, 0(a0)       # L :
142         br      stxcpy_aligned  # L0 : Latency=3
143         nop
144         nop
145
146
147 /* The source and destination are not co-aligned.  Align the destination
148    and cope.  We have to be very careful about not reading too much and
149    causing a SEGV.  */
150
151         .align 4
152 $u_head:
153         /* We know just enough now to be able to assemble the first
154            full source word.  We can still find a zero at the end of it
155            that prevents us from outputting the whole thing.
156
157            On entry to this basic block:
158            t0 == the first dest word, for masking back in, if needed else 0
159            t1 == the low bits of the first source word
160            t6 == bytemask that is -1 in dest word bytes */
161
162         ldq_u   t2, 8(a1)       # L :
163         addq    a1, 8, a1       # E :
164         extql   t1, a1, t1      # U : (stall on a1)
165         extqh   t2, a1, t4      # U : (stall on a1)
166
167         mskql   t0, a0, t0      # U :
168         or      t1, t4, t1      # E :
169         mskqh   t1, a0, t1      # U : (stall on t1)
170         or      t0, t1, t1      # E : (stall on t1)
171
172         or      t1, t6, t6      # E :
173         cmpbge  zero, t6, t8    # E : (stall)
174         lda     t6, -1          # E : for masking just below
175         bne     t8, $u_final    # U : (stall)
176
177         mskql   t6, a1, t6              # U : mask out the bits we have
178         or      t6, t2, t2              # E :   already extracted before (stall)
179         cmpbge  zero, t2, t8            # E :   testing eos (stall)
180         bne     t8, $u_late_head_exit   # U : (stall)
181
182         /* Finally, we've got all the stupid leading edge cases taken care
183            of and we can set up to enter the main loop.  */
184
185         stq_u   t1, 0(a0)       # L : store first output word
186         addq    a0, 8, a0       # E :
187         extql   t2, a1, t0      # U : position ho-bits of lo word
188         ldq_u   t2, 8(a1)       # U : read next high-order source word
189
190         addq    a1, 8, a1       # E :
191         cmpbge  zero, t2, t8    # E : (stall for t2)
192         nop                     # E :
193         bne     t8, $u_eos      # U : (stall)
194
195         /* Unaligned copy main loop.  In order to avoid reading too much,
196            the loop is structured to detect zeros in aligned source words.
197            This has, unfortunately, effectively pulled half of a loop
198            iteration out into the head and half into the tail, but it does
199            prevent nastiness from accumulating in the very thing we want
200            to run as fast as possible.
201
202            On entry to this basic block:
203            t0 == the shifted high-order bits from the previous source word
204            t2 == the unshifted current source word
205
206            We further know that t2 does not contain a null terminator.  */
207
208         .align 3
209 $u_loop:
210         extqh   t2, a1, t1      # U : extract high bits for current word
211         addq    a1, 8, a1       # E : (stall)
212         extql   t2, a1, t3      # U : extract low bits for next time (stall)
213         addq    a0, 8, a0       # E :
214
215         or      t0, t1, t1      # E : current dst word now complete
216         ldq_u   t2, 0(a1)       # L : Latency=3 load high word for next time
217         stq_u   t1, -8(a0)      # L : save the current word (stall)
218         mov     t3, t0          # E :
219
220         cmpbge  zero, t2, t8    # E : test new word for eos
221         beq     t8, $u_loop     # U : (stall)
222         nop
223         nop
224
225         /* We've found a zero somewhere in the source word we just read.
226            If it resides in the lower half, we have one (probably partial)
227            word to write out, and if it resides in the upper half, we
228            have one full and one partial word left to write out.
229
230            On entry to this basic block:
231            t0 == the shifted high-order bits from the previous source word
232            t2 == the unshifted current source word.  */
233 $u_eos:
234         extqh   t2, a1, t1      # U :
235         or      t0, t1, t1      # E : first (partial) source word complete (stall)
236         cmpbge  zero, t1, t8    # E : is the null in this first bit? (stall)
237         bne     t8, $u_final    # U : (stall)
238
239 $u_late_head_exit:
240         stq_u   t1, 0(a0)       # L : the null was in the high-order bits
241         addq    a0, 8, a0       # E :
242         extql   t2, a1, t1      # U :
243         cmpbge  zero, t1, t8    # E : (stall)
244
245         /* Take care of a final (probably partial) result word.
246            On entry to this basic block:
247            t1 == assembled source word
248            t8 == cmpbge mask that found the null.  */
249 $u_final:
250         negq    t8, t6          # E : isolate low bit set
251         and     t6, t8, t10     # E : (stall)
252         and     t10, 0x80, t6   # E : avoid dest word load if we can (stall)
253         bne     t6, 1f          # U : (stall)
254
255         ldq_u   t0, 0(a0)       # E :
256         subq    t10, 1, t6      # E :
257         or      t6, t10, t8     # E : (stall)
258         zapnot  t1, t6, t1      # U : kill source bytes >= null (stall)
259
260         zap     t0, t8, t0      # U : kill dest bytes <= null (2 cycle data stall)
261         or      t0, t1, t1      # E : (stall)
262         nop
263         nop
264
265 1:      stq_u   t1, 0(a0)       # L :
266         ret     (t9)            # L0 : Latency=3
267         nop
268         nop
269
270         /* Unaligned copy entry point.  */
271         .align 4
272 $unaligned:
273
274         ldq_u   t1, 0(a1)       # L : load first source word
275         and     a0, 7, t4       # E : find dest misalignment
276         and     a1, 7, t5       # E : find src misalignment
277         /* Conditionally load the first destination word and a bytemask
278            with 0xff indicating that the destination byte is sacrosanct.  */
279         mov     zero, t0        # E :
280
281         mov     zero, t6        # E :
282         beq     t4, 1f          # U :
283         ldq_u   t0, 0(a0)       # L :
284         lda     t6, -1          # E :
285
286         mskql   t6, a0, t6      # U :
287         nop
288         nop
289         nop
290 1:
291         subq    a1, t4, a1      # E : sub dest misalignment from src addr
292         /* If source misalignment is larger than dest misalignment, we need
293            extra startup checks to avoid SEGV.  */
294         cmplt   t4, t5, t10     # E :
295         beq     t10, $u_head    # U :
296         lda     t2, -1          # E : mask out leading garbage in source
297
298         mskqh   t2, t5, t2      # U :
299         ornot   t1, t2, t3      # E : (stall)
300         cmpbge  zero, t3, t8    # E : is there a zero? (stall)
301         beq     t8, $u_head     # U : (stall)
302
303         /* At this point we've found a zero in the first partial word of
304            the source.  We need to isolate the valid source data and mask
305            it into the original destination data.  (Incidentally, we know
306            that we'll need at least one byte of that original dest word.) */
307
308         ldq_u   t0, 0(a0)       # L :
309         negq    t8, t6          # E : build bitmask of bytes <= zero
310         and     t6, t8, t10     # E : (stall)
311         and     a1, 7, t5       # E :
312
313         subq    t10, 1, t6      # E :
314         or      t6, t10, t8     # E : (stall)
315         srl     t10, t5, t10    # U : adjust final null return value
316         zapnot  t2, t8, t2      # U : prepare source word; mirror changes (stall)
317
318         and     t1, t2, t1      # E : to source validity mask
319         extql   t2, a1, t2      # U :
320         extql   t1, a1, t1      # U : (stall)
321         andnot  t0, t2, t0      # .. e1 : zero place for source to reside (stall)
322
323         or      t0, t1, t1      # e1    : and put it there
324         stq_u   t1, 0(a0)       # .. e0 : (stall)
325         ret     (t9)            # e1    :
326         nop
327
328         .end __stxcpy
329