more debug output
[linux-2.4.git] / arch / ia64 / lib / memcpy.S
1 /*
2  *
3  * Optimized version of the standard memcpy() function
4  *
5  * Inputs:
6  *      in0:    destination address
7  *      in1:    source address
8  *      in2:    number of bytes to copy
9  * Output:
10  *      no return value
11  *
12  * Copyright (C) 2000-2001 Hewlett-Packard Co
13  *      Stephane Eranian <eranian@hpl.hp.com>
14  *      David Mosberger-Tang <davidm@hpl.hp.com>
15  */
16 #include <linux/config.h>
17
18 #include <asm/asmmacro.h>
19
20 GLOBAL_ENTRY(bcopy)
21         .regstk 3,0,0,0
22         mov r8=in0
23         mov in0=in1
24         ;;
25         mov in1=r8
26         // gas doesn't handle control flow across procedures, so it doesn't
27         // realize that a stop bit is needed before the "alloc" instruction
28         // below
29 {
30         nop.m 0
31         nop.f 0
32         nop.i 0
33 }       ;;
34 END(bcopy)
35         // FALL THROUGH
36 GLOBAL_ENTRY(memcpy)
37
38 #       define MEM_LAT  21              /* latency to memory */
39
40 #       define dst      r2
41 #       define src      r3
42 #       define retval   r8
43 #       define saved_pfs r9
44 #       define saved_lc r10
45 #       define saved_pr r11
46 #       define cnt      r16
47 #       define src2     r17
48 #       define t0       r18
49 #       define t1       r19
50 #       define t2       r20
51 #       define t3       r21
52 #       define t4       r22
53 #       define src_end  r23
54
55 #       define N        (MEM_LAT + 4)
56 #       define Nrot     ((N + 7) & ~7)
57
58         /*
59          * First, check if everything (src, dst, len) is a multiple of eight.  If
60          * so, we handle everything with no taken branches (other than the loop
61          * itself) and a small icache footprint.  Otherwise, we jump off to
62          * the more general copy routine handling arbitrary
63          * sizes/alignment etc.
64          */
65         .prologue
66         .save ar.pfs, saved_pfs
67         alloc saved_pfs=ar.pfs,3,Nrot,0,Nrot
68         .save ar.lc, saved_lc
69         mov saved_lc=ar.lc
70         or t0=in0,in1
71         ;;
72
73         or t0=t0,in2
74         .save pr, saved_pr
75         mov saved_pr=pr
76
77         .body
78
79         cmp.eq p6,p0=in2,r0     // zero length?
80         mov retval=in0          // return dst
81 (p6)    br.ret.spnt.many rp     // zero length, return immediately
82         ;;
83
84         mov dst=in0             // copy because of rotation
85         shr.u cnt=in2,3         // number of 8-byte words to copy
86         mov pr.rot=1<<16
87         ;;
88
89         adds cnt=-1,cnt         // br.ctop is repeat/until
90         cmp.gtu p7,p0=16,in2    // copying less than 16 bytes?
91         mov ar.ec=N
92         ;;
93
94         and t0=0x7,t0
95         mov ar.lc=cnt
96         ;;
97         cmp.ne p6,p0=t0,r0
98
99         mov src=in1             // copy because of rotation
100 (p7)    br.cond.spnt.few .memcpy_short
101 (p6)    br.cond.spnt.few .memcpy_long
102         ;;
103         nop.m   0
104         ;;
105         nop.m   0
106         nop.i   0
107         ;;
108         nop.m   0
109         ;;
110         .rotr val[N]
111         .rotp p[N]
112         .align 32
113 1: { .mib
114 (p[0])  ld8 val[0]=[src],8
115         nop.i 0
116         brp.loop.imp 1b, 2f
117 }
118 2: { .mfb
119 (p[N-1])st8 [dst]=val[N-1],8
120         nop.f 0
121         br.ctop.dptk.few 1b
122 }
123         ;;
124         mov ar.lc=saved_lc
125         mov pr=saved_pr,-1
126         mov ar.pfs=saved_pfs
127         br.ret.sptk.many rp
128
129         /*
130          * Small (<16 bytes) unaligned copying is done via a simple byte-at-the-time
131          * copy loop.  This performs relatively poorly on Itanium, but it doesn't
132          * get used very often (gcc inlines small copies) and due to atomicity
133          * issues, we want to avoid read-modify-write of entire words.
134          */
135         .align 32
136 .memcpy_short:
137         adds cnt=-1,in2         // br.ctop is repeat/until
138         mov ar.ec=MEM_LAT
139         brp.loop.imp 1f, 2f
140         ;;
141         mov ar.lc=cnt
142         ;;
143         nop.m   0
144         ;;
145         nop.m   0
146         nop.i   0
147         ;;
148         nop.m   0
149         ;;
150         nop.m   0
151         ;;
152         /*
153          * It is faster to put a stop bit in the loop here because it makes
154          * the pipeline shorter (and latency is what matters on short copies).
155          */
156         .align 32
157 1: { .mib
158 (p[0])  ld1 val[0]=[src],1
159         nop.i 0
160         brp.loop.imp 1b, 2f
161 } ;;
162 2: { .mfb
163 (p[MEM_LAT-1])st1 [dst]=val[MEM_LAT-1],1
164         nop.f 0
165         br.ctop.dptk.few 1b
166 } ;;
167         mov ar.lc=saved_lc
168         mov pr=saved_pr,-1
169         mov ar.pfs=saved_pfs
170         br.ret.sptk.many rp
171
172         /*
173          * Large (>= 16 bytes) copying is done in a fancy way.  Latency isn't
174          * an overriding concern here, but throughput is.  We first do
175          * sub-word copying until the destination is aligned, then we check
176          * if the source is also aligned.  If so, we do a simple load/store-loop
177          * until there are less than 8 bytes left over and then we do the tail,
178          * by storing the last few bytes using sub-word copying.  If the source
179          * is not aligned, we branch off to the non-congruent loop.
180          *
181          *   stage:   op:
182          *         0  ld
183          *         :
184          * MEM_LAT+3  shrp
185          * MEM_LAT+4  st
186          *
187          * On Itanium, the pipeline itself runs without stalls.  However,  br.ctop
188          * seems to introduce an unavoidable bubble in the pipeline so the overall
189          * latency is 2 cycles/iteration.  This gives us a _copy_ throughput
190          * of 4 byte/cycle.  Still not bad.
191          */
192 #       undef N
193 #       undef Nrot
194 #       define N        (MEM_LAT + 5)           /* number of stages */
195 #       define Nrot     ((N+1 + 2 + 7) & ~7)    /* number of rotating regs */
196
197 #define LOG_LOOP_SIZE   6
198
199 .memcpy_long:
200         alloc t3=ar.pfs,3,Nrot,0,Nrot   // resize register frame
201         and t0=-8,src           // t0 = src & ~7
202         and t2=7,src            // t2 = src & 7
203         ;;
204         ld8 t0=[t0]             // t0 = 1st source word
205         adds src2=7,src         // src2 = (src + 7)
206         sub t4=r0,dst           // t4 = -dst
207         ;;
208         and src2=-8,src2        // src2 = (src + 7) & ~7
209         shl t2=t2,3             // t2 = 8*(src & 7)
210         shl t4=t4,3             // t4 = 8*(dst & 7)
211         ;;
212         ld8 t1=[src2]           // t1 = 1st source word if src is 8-byte aligned, 2nd otherwise
213         sub t3=64,t2            // t3 = 64-8*(src & 7)
214         shr.u t0=t0,t2
215         ;;
216         add src_end=src,in2
217         shl t1=t1,t3
218         mov pr=t4,0x38          // (p5,p4,p3)=(dst & 7)
219         ;;
220         or t0=t0,t1
221         mov cnt=r0
222         adds src_end=-1,src_end
223         ;;
224 (p3)    st1 [dst]=t0,1
225 (p3)    shr.u t0=t0,8
226 (p3)    adds cnt=1,cnt
227         ;;
228 (p4)    st2 [dst]=t0,2
229 (p4)    shr.u t0=t0,16
230 (p4)    adds cnt=2,cnt
231         ;;
232 (p5)    st4 [dst]=t0,4
233 (p5)    adds cnt=4,cnt
234         and src_end=-8,src_end  // src_end = last word of source buffer
235         ;;
236
237         // At this point, dst is aligned to 8 bytes and there at least 16-7=9 bytes left to copy:
238
239 1:{     add src=cnt,src                 // make src point to remainder of source buffer
240         sub cnt=in2,cnt                 // cnt = number of bytes left to copy
241         mov t4=ip
242   }     ;;
243         and src2=-8,src                 // align source pointer
244         adds t4=.memcpy_loops-1b,t4
245         mov ar.ec=N
246
247         and t0=7,src                    // t0 = src & 7
248         shr.u t2=cnt,3                  // t2 = number of 8-byte words left to copy
249         shl cnt=cnt,3                   // move bits 0-2 to 3-5
250         ;;
251
252         .rotr val[N+1], w[2]
253         .rotp p[N]
254
255         cmp.ne p6,p0=t0,r0              // is src aligned, too?
256         shl t0=t0,LOG_LOOP_SIZE         // t0 = 8*(src & 7)
257         adds t2=-1,t2                   // br.ctop is repeat/until
258         ;;
259         add t4=t0,t4
260         mov pr=cnt,0x38                 // set (p5,p4,p3) to # of bytes last-word bytes to copy
261         mov ar.lc=t2
262         ;;
263         nop.m   0
264         ;;
265         nop.m   0
266         nop.i   0
267         ;;
268         nop.m   0
269         ;;
270 (p6)    ld8 val[1]=[src2],8             // prime the pump...
271         mov b6=t4
272         br.sptk.few b6
273         ;;
274
275 .memcpy_tail:
276         // At this point, (p5,p4,p3) are set to the number of bytes left to copy (which is
277         // less than 8) and t0 contains the last few bytes of the src buffer:
278 (p5)    st4 [dst]=t0,4
279 (p5)    shr.u t0=t0,32
280         mov ar.lc=saved_lc
281         ;;
282 (p4)    st2 [dst]=t0,2
283 (p4)    shr.u t0=t0,16
284         mov ar.pfs=saved_pfs
285         ;;
286 (p3)    st1 [dst]=t0
287         mov pr=saved_pr,-1
288         br.ret.sptk.many rp
289
290 ///////////////////////////////////////////////////////
291         .align 64
292
293 #define COPY(shift,index)                                                                       \
294  1: { .mib                                                                                      \
295         (p[0])          ld8 val[0]=[src2],8;                                                    \
296         (p[MEM_LAT+3])  shrp w[0]=val[MEM_LAT+3],val[MEM_LAT+4-index],shift;                    \
297                         brp.loop.imp 1b, 2f                                                     \
298     };                                                                                          \
299  2: { .mfb                                                                                      \
300         (p[MEM_LAT+4])  st8 [dst]=w[1],8;                                                       \
301                         nop.f 0;                                                                \
302                         br.ctop.dptk.few 1b;                                                    \
303     };                                                                                          \
304                         ;;                                                                      \
305                         ld8 val[N-1]=[src_end]; /* load last word (may be same as val[N]) */    \
306                         ;;                                                                      \
307                         shrp t0=val[N-1],val[N-index],shift;                                    \
308                         br .memcpy_tail
309 .memcpy_loops:
310         COPY(0, 1) /* no point special casing this---it doesn't go any faster without shrp */
311         COPY(8, 0)
312         COPY(16, 0)
313         COPY(24, 0)
314         COPY(32, 0)
315         COPY(40, 0)
316         COPY(48, 0)
317         COPY(56, 0)
318
319 END(memcpy)