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