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