Full support for Ginger Console
[linux-ginger.git] / arch / alpha / lib / ev6-stxcpy.S
blob4643ff2ffc8d1871b63c068a96935933c7b9e3c1
1 /*
2  * arch/alpha/lib/ev6-stxcpy.S
3  * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com>
4  *
5  * Copy a null-terminated string from SRC to DST.
6  *
7  * This is an internal routine used by strcpy, stpcpy, and strcat.
8  * As such, it uses special linkage conventions to make implementation
9  * of these public functions more efficient.
10  *
11  * On input:
12  *      t9 = return address
13  *      a0 = DST
14  *      a1 = SRC
15  *
16  * On output:
17  *      t12 = bitmask (with one bit set) indicating the last byte written
18  *      a0  = unaligned address of the last *word* written
19  *
20  * Furthermore, v0, a3-a5, t11, and t12 are untouched.
21  *
22  * Much of the information about 21264 scheduling/coding comes from:
23  *      Compiler Writer's Guide for the Alpha 21264
24  *      abbreviated as 'CWG' in other comments here
25  *      ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
26  * Scheduling notation:
27  *      E       - either cluster
28  *      U       - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
29  *      L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
30  * Try not to change the actual algorithm if possible for consistency.
31  */
33 #include <asm/regdef.h>
35         .set noat
36         .set noreorder
38         .text
40 /* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that
41    doesn't like putting the entry point for a procedure somewhere in the
42    middle of the procedure descriptor.  Work around this by putting the
43    aligned copy in its own procedure descriptor */
46         .ent stxcpy_aligned
47         .align 4
48 stxcpy_aligned:
49         .frame sp, 0, t9
50         .prologue 0
52         /* On entry to this basic block:
53            t0 == the first destination word for masking back in
54            t1 == the first source word.  */
56         /* Create the 1st output word and detect 0's in the 1st input word.  */
57         lda     t2, -1          # E : build a mask against false zero
58         mskqh   t2, a1, t2      # U :   detection in the src word (stall)
59         mskqh   t1, a1, t3      # U :
60         ornot   t1, t2, t2      # E : (stall)
62         mskql   t0, a1, t0      # U : assemble the first output word
63         cmpbge  zero, t2, t8    # E : bits set iff null found
64         or      t0, t3, t1      # E : (stall)
65         bne     t8, $a_eos      # U : (stall)
67         /* On entry to this basic block:
68            t0 == the first destination word for masking back in
69            t1 == a source word not containing a null.  */
70         /* Nops here to separate store quads from load quads */
72 $a_loop:
73         stq_u   t1, 0(a0)       # L :
74         addq    a0, 8, a0       # E :
75         nop
76         nop
78         ldq_u   t1, 0(a1)       # L : Latency=3
79         addq    a1, 8, a1       # E :
80         cmpbge  zero, t1, t8    # E : (3 cycle stall)
81         beq     t8, $a_loop     # U : (stall for t8)
83         /* Take care of the final (partial) word store.
84            On entry to this basic block we have:
85            t1 == the source word containing the null
86            t8 == the cmpbge mask that found it.  */
87 $a_eos:
88         negq    t8, t6          # E : find low bit set
89         and     t8, t6, t12     # E : (stall)
90         /* For the sake of the cache, don't read a destination word
91            if we're not going to need it.  */
92         and     t12, 0x80, t6   # E : (stall)
93         bne     t6, 1f          # U : (stall)
95         /* We're doing a partial word store and so need to combine
96            our source and original destination words.  */
97         ldq_u   t0, 0(a0)       # L : Latency=3
98         subq    t12, 1, t6      # E :
99         zapnot  t1, t6, t1      # U : clear src bytes >= null (stall)
100         or      t12, t6, t8     # E : (stall)
102         zap     t0, t8, t0      # E : clear dst bytes <= null
103         or      t0, t1, t1      # E : (stall)
104         nop
105         nop
107 1:      stq_u   t1, 0(a0)       # L :
108         ret     (t9)            # L0 : Latency=3
109         nop
110         nop
112         .end stxcpy_aligned
114         .align 4
115         .ent __stxcpy
116         .globl __stxcpy
117 __stxcpy:
118         .frame sp, 0, t9
119         .prologue 0
121         /* Are source and destination co-aligned?  */
122         xor     a0, a1, t0      # E :
123         unop                    # E :
124         and     t0, 7, t0       # E : (stall)
125         bne     t0, $unaligned  # U : (stall)
127         /* We are co-aligned; take care of a partial first word.  */
128         ldq_u   t1, 0(a1)               # L : load first src word
129         and     a0, 7, t0               # E : take care not to load a word ...
130         addq    a1, 8, a1               # E :
131         beq     t0, stxcpy_aligned      # U : ... if we wont need it (stall)
133         ldq_u   t0, 0(a0)       # L :
134         br      stxcpy_aligned  # L0 : Latency=3
135         nop
136         nop
139 /* The source and destination are not co-aligned.  Align the destination
140    and cope.  We have to be very careful about not reading too much and
141    causing a SEGV.  */
143         .align 4
144 $u_head:
145         /* We know just enough now to be able to assemble the first
146            full source word.  We can still find a zero at the end of it
147            that prevents us from outputting the whole thing.
149            On entry to this basic block:
150            t0 == the first dest word, for masking back in, if needed else 0
151            t1 == the low bits of the first source word
152            t6 == bytemask that is -1 in dest word bytes */
154         ldq_u   t2, 8(a1)       # L :
155         addq    a1, 8, a1       # E :
156         extql   t1, a1, t1      # U : (stall on a1)
157         extqh   t2, a1, t4      # U : (stall on a1)
159         mskql   t0, a0, t0      # U :
160         or      t1, t4, t1      # E :
161         mskqh   t1, a0, t1      # U : (stall on t1)
162         or      t0, t1, t1      # E : (stall on t1)
164         or      t1, t6, t6      # E :
165         cmpbge  zero, t6, t8    # E : (stall)
166         lda     t6, -1          # E : for masking just below
167         bne     t8, $u_final    # U : (stall)
169         mskql   t6, a1, t6              # U : mask out the bits we have
170         or      t6, t2, t2              # E :   already extracted before (stall)
171         cmpbge  zero, t2, t8            # E :   testing eos (stall)
172         bne     t8, $u_late_head_exit   # U : (stall)
174         /* Finally, we've got all the stupid leading edge cases taken care
175            of and we can set up to enter the main loop.  */
177         stq_u   t1, 0(a0)       # L : store first output word
178         addq    a0, 8, a0       # E :
179         extql   t2, a1, t0      # U : position ho-bits of lo word
180         ldq_u   t2, 8(a1)       # U : read next high-order source word
182         addq    a1, 8, a1       # E :
183         cmpbge  zero, t2, t8    # E : (stall for t2)
184         nop                     # E :
185         bne     t8, $u_eos      # U : (stall)
187         /* Unaligned copy main loop.  In order to avoid reading too much,
188            the loop is structured to detect zeros in aligned source words.
189            This has, unfortunately, effectively pulled half of a loop
190            iteration out into the head and half into the tail, but it does
191            prevent nastiness from accumulating in the very thing we want
192            to run as fast as possible.
194            On entry to this basic block:
195            t0 == the shifted high-order bits from the previous source word
196            t2 == the unshifted current source word
198            We further know that t2 does not contain a null terminator.  */
200         .align 3
201 $u_loop:
202         extqh   t2, a1, t1      # U : extract high bits for current word
203         addq    a1, 8, a1       # E : (stall)
204         extql   t2, a1, t3      # U : extract low bits for next time (stall)
205         addq    a0, 8, a0       # E :
207         or      t0, t1, t1      # E : current dst word now complete
208         ldq_u   t2, 0(a1)       # L : Latency=3 load high word for next time
209         stq_u   t1, -8(a0)      # L : save the current word (stall)
210         mov     t3, t0          # E :
212         cmpbge  zero, t2, t8    # E : test new word for eos
213         beq     t8, $u_loop     # U : (stall)
214         nop
215         nop
217         /* We've found a zero somewhere in the source word we just read.
218            If it resides in the lower half, we have one (probably partial)
219            word to write out, and if it resides in the upper half, we
220            have one full and one partial word left to write out.
222            On entry to this basic block:
223            t0 == the shifted high-order bits from the previous source word
224            t2 == the unshifted current source word.  */
225 $u_eos:
226         extqh   t2, a1, t1      # U :
227         or      t0, t1, t1      # E : first (partial) source word complete (stall)
228         cmpbge  zero, t1, t8    # E : is the null in this first bit? (stall)
229         bne     t8, $u_final    # U : (stall)
231 $u_late_head_exit:
232         stq_u   t1, 0(a0)       # L : the null was in the high-order bits
233         addq    a0, 8, a0       # E :
234         extql   t2, a1, t1      # U :
235         cmpbge  zero, t1, t8    # E : (stall)
237         /* Take care of a final (probably partial) result word.
238            On entry to this basic block:
239            t1 == assembled source word
240            t8 == cmpbge mask that found the null.  */
241 $u_final:
242         negq    t8, t6          # E : isolate low bit set
243         and     t6, t8, t12     # E : (stall)
244         and     t12, 0x80, t6   # E : avoid dest word load if we can (stall)
245         bne     t6, 1f          # U : (stall)
247         ldq_u   t0, 0(a0)       # E :
248         subq    t12, 1, t6      # E :
249         or      t6, t12, t8     # E : (stall)
250         zapnot  t1, t6, t1      # U : kill source bytes >= null (stall)
252         zap     t0, t8, t0      # U : kill dest bytes <= null (2 cycle data stall)
253         or      t0, t1, t1      # E : (stall)
254         nop
255         nop
257 1:      stq_u   t1, 0(a0)       # L :
258         ret     (t9)            # L0 : Latency=3
259         nop
260         nop
262         /* Unaligned copy entry point.  */
263         .align 4
264 $unaligned:
266         ldq_u   t1, 0(a1)       # L : load first source word
267         and     a0, 7, t4       # E : find dest misalignment
268         and     a1, 7, t5       # E : find src misalignment
269         /* Conditionally load the first destination word and a bytemask
270            with 0xff indicating that the destination byte is sacrosanct.  */
271         mov     zero, t0        # E :
273         mov     zero, t6        # E :
274         beq     t4, 1f          # U :
275         ldq_u   t0, 0(a0)       # L :
276         lda     t6, -1          # E :
278         mskql   t6, a0, t6      # U :
279         nop
280         nop
281         nop
283         subq    a1, t4, a1      # E : sub dest misalignment from src addr
284         /* If source misalignment is larger than dest misalignment, we need
285            extra startup checks to avoid SEGV.  */
286         cmplt   t4, t5, t12     # E :
287         beq     t12, $u_head    # U :
288         lda     t2, -1          # E : mask out leading garbage in source
290         mskqh   t2, t5, t2      # U :
291         ornot   t1, t2, t3      # E : (stall)
292         cmpbge  zero, t3, t8    # E : is there a zero? (stall)
293         beq     t8, $u_head     # U : (stall)
295         /* At this point we've found a zero in the first partial word of
296            the source.  We need to isolate the valid source data and mask
297            it into the original destination data.  (Incidentally, we know
298            that we'll need at least one byte of that original dest word.) */
300         ldq_u   t0, 0(a0)       # L :
301         negq    t8, t6          # E : build bitmask of bytes <= zero
302         and     t6, t8, t12     # E : (stall)
303         and     a1, 7, t5       # E :
305         subq    t12, 1, t6      # E :
306         or      t6, t12, t8     # E : (stall)
307         srl     t12, t5, t12    # U : adjust final null return value
308         zapnot  t2, t8, t2      # U : prepare source word; mirror changes (stall)
310         and     t1, t2, t1      # E : to source validity mask
311         extql   t2, a1, t2      # U :
312         extql   t1, a1, t1      # U : (stall)
313         andnot  t0, t2, t0      # .. e1 : zero place for source to reside (stall)
315         or      t0, t1, t1      # e1    : and put it there
316         stq_u   t1, 0(a0)       # .. e0 : (stall)
317         ret     (t9)            # e1    :
318         nop
320         .end __stxcpy