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

source code of glibc/sysdeps/ia64/memcpy.S