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