(atomic_full_barrier): New.
[kopensolaris-gnu/glibc.git] / sysdeps / ia64 / memcpy.S
1 /* Optimized version of the standard memcpy() function.
2    This file is part of the GNU C Library.
3    Copyright (C) 2000, 2001, 2003 Free Software Foundation, Inc.
4    Contributed by Dan Pop for Itanium <Dan.Pop@cern.ch>.
5    Rewritten for McKinley by Sverre Jarp, HP Labs/CERN <Sverre.Jarp@cern.ch>
6
7    The GNU C Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11
12    The GNU C Library 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 GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with the GNU C Library; if not, write to the Free
19    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20    02111-1307 USA.  */
21
22 /* Return: dest
23
24    Inputs:
25         in0:    dest
26         in1:    src
27         in2:    byte count
28
29    An assembly implementation of the algorithm used by the generic C
30    version from glibc.  The case when source and sest are aligned is
31    treated separately, for extra performance.
32
33    In this form, memcpy assumes little endian mode.  For big endian mode,
34    sh1 must be computed using an extra instruction: sub sh1 = 64, sh1
35    and the order of r[MEMLAT] and r[MEMLAT+1] must be reverted in the
36    shrp instruction.  */
37
38 #define USE_LFETCH
39 #define USE_FLP
40 #include <sysdep.h>
41 #undef ret
42
43 #define LFETCH_DIST     500
44
45 #define ALIGN_UNROLL_no   4 // no. of elements
46 #define ALIGN_UNROLL_sh   2 // (shift amount)
47
48 #define MEMLAT  8
49 #define Nrot    ((4*(MEMLAT+2) + 7) & ~7)
50
51 #define OP_T_THRES      16
52 #define OPSIZ           8
53
54 #define loopcnt         r14
55 #define elemcnt         r15
56 #define saved_pr        r16
57 #define saved_lc        r17
58 #define adest           r18
59 #define dest            r19
60 #define asrc            r20
61 #define src             r21
62 #define len             r22
63 #define tmp2            r23
64 #define tmp3            r24
65 #define tmp4            r25
66 #define ptable          r26
67 #define ploop56         r27
68 #define loopaddr        r28
69 #define sh1             r29
70 #define ptr1            r30
71 #define ptr2            r31
72
73 #define movi0           mov
74
75 #define p_scr           p6
76 #define p_xtr           p7
77 #define p_nxtr          p8
78 #define p_few           p9
79
80 #if defined(USE_FLP)
81 #define load            ldf8
82 #define store           stf8
83 #define tempreg         f6
84 #define the_r           fr
85 #define the_s           fs
86 #define the_t           ft
87 #define the_q           fq
88 #define the_w           fw
89 #define the_x           fx
90 #define the_y           fy
91 #define the_z           fz
92 #elif defined(USE_INT)
93 #define load            ld8
94 #define store           st8
95 #define tempreg         tmp2
96 #define the_r           r
97 #define the_s           s
98 #define the_t           t
99 #define the_q           q
100 #define the_w           w
101 #define the_x           x
102 #define the_y           y
103 #define the_z           z
104 #endif
105
106
107 #if defined(USE_LFETCH)
108 #define LOOP(shift)                                             \
109                 .align  32 ;                                    \
110 .loop##shift##:                                                 \
111 { .mmb                                                          \
112 (p[0])  ld8.nt1 r[0] = [asrc], 8 ;                              \
113 (p[0])  lfetch.nt1 [ptr1], 16 ;                         \
114         nop.b 0 ;                                               \
115 } { .mib                                                        \
116 (p[MEMLAT+1]) st8 [dest] = tmp3, 8 ;                            \
117 (p[MEMLAT]) shrp tmp3 = r[MEMLAT], s[MEMLAT+1], shift ;         \
118         nop.b 0 ;;                                              \
119  } { .mmb                                                       \
120 (p[0])  ld8.nt1 s[0] = [asrc], 8 ;                              \
121 (p[0])  lfetch.nt1      [ptr2], 16 ;                    \
122         nop.b 0 ;                                               \
123 } { .mib                                                        \
124 (p[MEMLAT+1]) st8 [dest] = tmp4, 8 ;                            \
125 (p[MEMLAT]) shrp tmp4 = s[MEMLAT], r[MEMLAT], shift ;           \
126         br.ctop.sptk.many .loop##shift                          \
127 ;; }                                                            \
128 { .mib                                                          \
129         br.cond.sptk.many .copy_bytes ; /* deal with the remaining bytes */  \
130 }
131 #else
132 #define LOOP(shift)                                             \
133                 .align  32 ;                                    \
134 .loop##shift##:                                                 \
135 { .mmb                                                          \
136 (p[0])  ld8.nt1 r[0] = [asrc], 8 ;                              \
137         nop.b 0 ;                                               \
138 } { .mib                                                        \
139 (p[MEMLAT+1]) st8 [dest] = tmp3, 8 ;                            \
140 (p[MEMLAT]) shrp tmp3 = r[MEMLAT], s[MEMLAT+1], shift ;         \
141         nop.b 0 ;;                                              \
142  } { .mmb                                                       \
143 (p[0])  ld8.nt1 s[0] = [asrc], 8 ;                              \
144         nop.b 0 ;                                               \
145 } { .mib                                                        \
146 (p[MEMLAT+1]) st8 [dest] = tmp4, 8 ;                            \
147 (p[MEMLAT]) shrp tmp4 = s[MEMLAT], r[MEMLAT], shift ;           \
148         br.ctop.sptk.many .loop##shift                          \
149 ;; }                                                            \
150 { .mib                                                          \
151         br.cond.sptk.many .copy_bytes ; /* deal with the remaining bytes */  \
152 }
153 #endif
154
155
156 ENTRY(memcpy)
157 { .mmi
158         .prologue
159         alloc   r2 = ar.pfs, 3, Nrot - 3, 0, Nrot
160         .rotr   r[MEMLAT+1], s[MEMLAT+2], q[MEMLAT+1], t[MEMLAT+1]
161         .rotp   p[MEMLAT+2]
162         .rotf   fr[MEMLAT+1], fq[MEMLAT+1], fs[MEMLAT+1], ft[MEMLAT+1]
163         mov     ret0 = in0              // return tmp2 = dest
164         .save   pr, saved_pr
165         movi0   saved_pr = pr           // save the predicate registers
166 } { .mmi
167         and     tmp4 = 7, in0           // check if destination is aligned
168         mov     dest = in0              // dest
169         mov     src = in1               // src
170 ;; }
171 { .mii
172         cmp.eq  p_scr, p0 = in2, r0     // if (len == 0)
173         .save   ar.lc, saved_lc
174         movi0   saved_lc = ar.lc        // save the loop counter
175         .body
176         cmp.ge  p_few, p0 = OP_T_THRES, in2 // is len <= OP_T_THRESH
177 } { .mbb
178         mov     len = in2               // len
179 (p_scr) br.cond.dpnt.few .restore_and_exit //   Branch no. 1: return dest
180 (p_few) br.cond.dpnt.many .copy_bytes   // Branch no. 2: copy byte by byte
181 ;; }
182 { .mmi
183 #if defined(USE_LFETCH)
184         lfetch.nt1 [dest]               //
185         lfetch.nt1 [src]                //
186 #endif
187         shr.u   elemcnt = len, 3        // elemcnt = len / 8
188 } { .mib
189         cmp.eq  p_scr, p0 = tmp4, r0    // is destination aligned?
190         sub     loopcnt = 7, tmp4       //
191 (p_scr) br.cond.dptk.many .dest_aligned
192 ;; }
193 { .mmi
194         ld1     tmp2 = [src], 1         //
195         sub     len = len, loopcnt, 1   // reduce len
196         movi0   ar.lc = loopcnt         //
197 } { .mib
198         cmp.ne  p_scr, p0 = 0, loopcnt  // avoid loading beyond end-point
199 ;; }
200
201 .l0:    // ---------------------------- // L0: Align src on 8-byte boundary
202 { .mmi
203         st1     [dest] = tmp2, 1        //
204 (p_scr) ld1     tmp2 = [src], 1         //
205 } { .mib
206         cmp.lt  p_scr, p0 = 1, loopcnt  // avoid load beyond end-point
207         add     loopcnt = -1, loopcnt
208         br.cloop.dptk.few .l0           //
209 ;; }
210
211 .dest_aligned:
212 { .mmi
213         and     tmp4 = 7, src           // ready for alignment check
214         shr.u   elemcnt = len, 3        // elemcnt = len / 8
215 ;; }
216 { .mib
217         cmp.ne  p_scr, p0 = tmp4, r0    // is source also aligned
218         tbit.nz p_xtr, p_nxtr = src, 3  // prepare a separate move if src
219 } { .mib                                // is not 16B aligned
220         add     ptr2 = LFETCH_DIST, dest        // prefetch address
221         add     ptr1 = LFETCH_DIST, src
222 (p_scr) br.cond.dptk.many .src_not_aligned
223 ;; }
224
225 // The optimal case, when dest, and src are aligned
226
227 .both_aligned:
228 { .mmi
229         .pred.rel "mutex",p_xtr,p_nxtr
230 (p_xtr) cmp.gt  p_scr, p0 = ALIGN_UNROLL_no+1, elemcnt // Need N + 1 to qualify
231 (p_nxtr) cmp.gt p_scr, p0 = ALIGN_UNROLL_no, elemcnt  // Need only N to qualify
232         movi0   pr.rot = 1 << 16        // set rotating predicates
233 } { .mib
234 (p_scr) br.cond.dpnt.many .copy_full_words
235 ;; }
236
237 { .mmi
238 (p_xtr) load    tempreg = [src], 8
239 (p_xtr) add     elemcnt = -1, elemcnt
240         movi0   ar.ec = MEMLAT + 1      // set the epilog counter
241 ;; }
242 { .mmi
243 (p_xtr) add     len = -8, len           //
244         add     asrc = 16, src          // one bank apart (for USE_INT)
245         shr.u   loopcnt = elemcnt, ALIGN_UNROLL_sh  // cater for unrolling
246 ;;}
247 { .mmi
248         add     loopcnt = -1, loopcnt
249 (p_xtr) store   [dest] = tempreg, 8     // copy the "extra" word
250         nop.i   0
251 ;; }
252 { .mib
253         add     adest = 16, dest
254         movi0   ar.lc = loopcnt         // set the loop counter
255 ;; }
256
257         .align  32
258 #if defined(USE_FLP)
259 .l1: // ------------------------------- // L1: Everything a multiple of 8
260 { .mmi
261 #if defined(USE_LFETCH)
262 (p[0])  lfetch.nt1 [ptr2],32
263 #endif
264 (p[0])  ldfp8   the_r[0],the_q[0] = [src], 16
265 (p[0])  add     len = -32, len
266 } {.mmb
267 (p[MEMLAT]) store [dest] = the_r[MEMLAT], 8
268 (p[MEMLAT]) store [adest] = the_s[MEMLAT], 8
269 ;; }
270 { .mmi
271 #if defined(USE_LFETCH)
272 (p[0])  lfetch.nt1 [ptr1],32
273 #endif
274 (p[0])  ldfp8   the_s[0], the_t[0] = [src], 16
275 } {.mmb
276 (p[MEMLAT]) store [dest] = the_q[MEMLAT], 24
277 (p[MEMLAT]) store [adest] = the_t[MEMLAT], 24
278         br.ctop.dptk.many .l1
279 ;; }
280 #elif defined(USE_INT)
281 .l1: // ------------------------------- // L1: Everything a multiple of 8
282 { .mmi
283 (p[0])  load    the_r[0] = [src], 8
284 (p[0])  load    the_q[0] = [asrc], 8
285 (p[0])  add     len = -32, len
286 } {.mmb
287 (p[MEMLAT]) store [dest] = the_r[MEMLAT], 8
288 (p[MEMLAT]) store [adest] = the_q[MEMLAT], 8
289 ;; }
290 { .mmi
291 (p[0])  load    the_s[0]  = [src], 24
292 (p[0])  load    the_t[0] = [asrc], 24
293 } {.mmb
294 (p[MEMLAT]) store [dest] = the_s[MEMLAT], 24
295 (p[MEMLAT]) store [adest] = the_t[MEMLAT], 24
296 #if defined(USE_LFETCH)
297 ;; }
298 { .mmb
299 (p[0])  lfetch.nt1 [ptr2],32
300 (p[0])  lfetch.nt1 [ptr1],32
301 #endif
302         br.ctop.dptk.many .l1
303 ;; }
304 #endif
305
306 .copy_full_words:
307 { .mib
308         cmp.gt  p_scr, p0 = 8, len      //
309         shr.u   elemcnt = len, 3        //
310 (p_scr) br.cond.dpnt.many .copy_bytes
311 ;; }
312 { .mii
313         load    tempreg = [src], 8
314         add     loopcnt = -1, elemcnt   //
315 ;; }
316 { .mii
317         cmp.ne  p_scr, p0 = 0, loopcnt  //
318         mov     ar.lc = loopcnt         //
319 ;; }
320
321 .l2: // ------------------------------- // L2: Max 4 words copied separately
322 { .mmi
323         store   [dest] = tempreg, 8
324 (p_scr) load    tempreg = [src], 8      //
325         add     len = -8, len
326 } { .mib
327         cmp.lt  p_scr, p0 = 1, loopcnt  // avoid load beyond end-point
328         add     loopcnt = -1, loopcnt
329         br.cloop.dptk.few  .l2
330 ;; }
331
332 .copy_bytes:
333 { .mib
334         cmp.eq  p_scr, p0 = len, r0     // is len == 0 ?
335         add     loopcnt = -1, len       // len--;
336 (p_scr) br.cond.spnt    .restore_and_exit
337 ;; }
338 { .mii
339         ld1     tmp2 = [src], 1
340         movi0   ar.lc = loopcnt
341         cmp.ne  p_scr, p0 = 0, loopcnt  // avoid load beyond end-point
342 ;; }
343
344 .l3: // ------------------------------- // L3: Final byte move
345 { .mmi
346         st1     [dest] = tmp2, 1
347 (p_scr) ld1     tmp2 = [src], 1
348 } { .mib
349         cmp.lt  p_scr, p0 = 1, loopcnt  // avoid load beyond end-point
350         add     loopcnt = -1, loopcnt
351         br.cloop.dptk.few  .l3
352 ;; }
353
354 .restore_and_exit:
355 { .mmi
356         movi0   pr = saved_pr, -1       // restore the predicate registers
357 ;; }
358 { .mib
359         movi0   ar.lc = saved_lc        // restore the loop counter
360         br.ret.sptk.many b0
361 ;; }
362
363
364 .src_not_aligned:
365 { .mmi
366         cmp.gt  p_scr, p0 = 16, len
367         and     sh1 = 7, src            // sh1 = src % 8
368         shr.u   loopcnt = len, 4        // element-cnt = len / 16
369 } { .mib
370         add     tmp4 = @ltoff(.table), gp
371         add     tmp3 = @ltoff(.loop56), gp
372 (p_scr) br.cond.dpnt.many .copy_bytes   // do byte by byte if too few
373 ;; }
374 { .mmi
375         and     asrc = -8, src          // asrc = (-8) -- align src for loop
376         add     loopcnt = -1, loopcnt   // loopcnt--
377         shl     sh1 = sh1, 3            // sh1 = 8 * (src % 8)
378 } { .mmi
379         ld8     ptable = [tmp4]         // ptable = &table
380         ld8     ploop56 = [tmp3]        // ploop56 = &loop56
381         and     tmp2 = -16, len         // tmp2 = len & -OPSIZ
382 ;; }
383 { .mmi
384         add     tmp3 = ptable, sh1      // tmp3 = &table + sh1
385         add     src = src, tmp2         // src += len & (-16)
386         movi0   ar.lc = loopcnt         // set LC
387 ;; }
388 { .mmi
389         ld8     tmp4 = [tmp3]           // tmp4 = loop offset
390         sub     len = len, tmp2         // len -= len & (-16)
391         movi0   ar.ec = MEMLAT + 2      // one more pass needed
392 ;; }
393 { .mmi
394         ld8     s[1] = [asrc], 8        // preload
395         sub     loopaddr = ploop56,tmp4 // loopadd = &loop56 - loop offset
396         movi0   pr.rot = 1 << 16        // set rotating predicates
397 ;; }
398 { .mib
399         nop.m   0
400         movi0   b6 = loopaddr
401         br      b6                      // jump to the appropriate loop
402 ;; }
403
404         LOOP(8)
405         LOOP(16)
406         LOOP(24)
407         LOOP(32)
408         LOOP(40)
409         LOOP(48)
410         LOOP(56)
411 END(memcpy)
412 libc_hidden_builtin_def (memcpy)
413
414         .rodata
415         .align 8
416 .table:
417         data8   0                       // dummy entry
418         data8   .loop56 - .loop8
419         data8   .loop56 - .loop16
420         data8   .loop56 - .loop24
421         data8   .loop56 - .loop32
422         data8   .loop56 - .loop40
423         data8   .loop56 - .loop48
424         data8   .loop56 - .loop56