eal/x86: optimize memcpy for SSE and AVX
[dpdk.git] / lib / librte_eal / common / include / arch / x86 / rte_memcpy.h
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #ifndef _RTE_MEMCPY_X86_64_H_
35 #define _RTE_MEMCPY_X86_64_H_
36
37 /**
38  * @file
39  *
40  * Functions for SSE/AVX/AVX2 implementation of memcpy().
41  */
42
43 #include <stdio.h>
44 #include <stdint.h>
45 #include <string.h>
46 #include <x86intrin.h>
47
48 #ifdef __cplusplus
49 extern "C" {
50 #endif
51
52 /**
53  * Copy bytes from one location to another. The locations must not overlap.
54  *
55  * @note This is implemented as a macro, so it's address should not be taken
56  * and care is needed as parameter expressions may be evaluated multiple times.
57  *
58  * @param dst
59  *   Pointer to the destination of the data.
60  * @param src
61  *   Pointer to the source data.
62  * @param n
63  *   Number of bytes to copy.
64  * @return
65  *   Pointer to the destination data.
66  */
67 static inline void *
68 rte_memcpy(void *dst, const void *src, size_t n) __attribute__((always_inline));
69
70 #ifdef RTE_MACHINE_CPUFLAG_AVX2
71
72 /**
73  * AVX2 implementation below
74  */
75
76 /**
77  * Copy 16 bytes from one location to another,
78  * locations should not overlap.
79  */
80 static inline void
81 rte_mov16(uint8_t *dst, const uint8_t *src)
82 {
83         __m128i xmm0;
84
85         xmm0 = _mm_loadu_si128((const __m128i *)src);
86         _mm_storeu_si128((__m128i *)dst, xmm0);
87 }
88
89 /**
90  * Copy 32 bytes from one location to another,
91  * locations should not overlap.
92  */
93 static inline void
94 rte_mov32(uint8_t *dst, const uint8_t *src)
95 {
96         __m256i ymm0;
97
98         ymm0 = _mm256_loadu_si256((const __m256i *)src);
99         _mm256_storeu_si256((__m256i *)dst, ymm0);
100 }
101
102 /**
103  * Copy 64 bytes from one location to another,
104  * locations should not overlap.
105  */
106 static inline void
107 rte_mov64(uint8_t *dst, const uint8_t *src)
108 {
109         rte_mov32((uint8_t *)dst + 0 * 32, (const uint8_t *)src + 0 * 32);
110         rte_mov32((uint8_t *)dst + 1 * 32, (const uint8_t *)src + 1 * 32);
111 }
112
113 /**
114  * Copy 128 bytes from one location to another,
115  * locations should not overlap.
116  */
117 static inline void
118 rte_mov128(uint8_t *dst, const uint8_t *src)
119 {
120         rte_mov32((uint8_t *)dst + 0 * 32, (const uint8_t *)src + 0 * 32);
121         rte_mov32((uint8_t *)dst + 1 * 32, (const uint8_t *)src + 1 * 32);
122         rte_mov32((uint8_t *)dst + 2 * 32, (const uint8_t *)src + 2 * 32);
123         rte_mov32((uint8_t *)dst + 3 * 32, (const uint8_t *)src + 3 * 32);
124 }
125
126 /**
127  * Copy 256 bytes from one location to another,
128  * locations should not overlap.
129  */
130 static inline void
131 rte_mov256(uint8_t *dst, const uint8_t *src)
132 {
133         rte_mov32((uint8_t *)dst + 0 * 32, (const uint8_t *)src + 0 * 32);
134         rte_mov32((uint8_t *)dst + 1 * 32, (const uint8_t *)src + 1 * 32);
135         rte_mov32((uint8_t *)dst + 2 * 32, (const uint8_t *)src + 2 * 32);
136         rte_mov32((uint8_t *)dst + 3 * 32, (const uint8_t *)src + 3 * 32);
137         rte_mov32((uint8_t *)dst + 4 * 32, (const uint8_t *)src + 4 * 32);
138         rte_mov32((uint8_t *)dst + 5 * 32, (const uint8_t *)src + 5 * 32);
139         rte_mov32((uint8_t *)dst + 6 * 32, (const uint8_t *)src + 6 * 32);
140         rte_mov32((uint8_t *)dst + 7 * 32, (const uint8_t *)src + 7 * 32);
141 }
142
143 /**
144  * Copy 64-byte blocks from one location to another,
145  * locations should not overlap.
146  */
147 static inline void
148 rte_mov64blocks(uint8_t *dst, const uint8_t *src, size_t n)
149 {
150         __m256i ymm0, ymm1;
151
152         while (n >= 64) {
153                 ymm0 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 0 * 32));
154                 n -= 64;
155                 ymm1 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 1 * 32));
156                 src = (const uint8_t *)src + 64;
157                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 0 * 32), ymm0);
158                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 1 * 32), ymm1);
159                 dst = (uint8_t *)dst + 64;
160         }
161 }
162
163 /**
164  * Copy 256-byte blocks from one location to another,
165  * locations should not overlap.
166  */
167 static inline void
168 rte_mov256blocks(uint8_t *dst, const uint8_t *src, size_t n)
169 {
170         __m256i ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7;
171
172         while (n >= 256) {
173                 ymm0 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 0 * 32));
174                 n -= 256;
175                 ymm1 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 1 * 32));
176                 ymm2 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 2 * 32));
177                 ymm3 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 3 * 32));
178                 ymm4 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 4 * 32));
179                 ymm5 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 5 * 32));
180                 ymm6 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 6 * 32));
181                 ymm7 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 7 * 32));
182                 src = (const uint8_t *)src + 256;
183                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 0 * 32), ymm0);
184                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 1 * 32), ymm1);
185                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 2 * 32), ymm2);
186                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 3 * 32), ymm3);
187                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 4 * 32), ymm4);
188                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 5 * 32), ymm5);
189                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 6 * 32), ymm6);
190                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 7 * 32), ymm7);
191                 dst = (uint8_t *)dst + 256;
192         }
193 }
194
195 static inline void *
196 rte_memcpy(void *dst, const void *src, size_t n)
197 {
198         void *ret = dst;
199         int dstofss;
200         int bits;
201
202         /**
203          * Copy less than 16 bytes
204          */
205         if (n < 16) {
206                 if (n & 0x01) {
207                         *(uint8_t *)dst = *(const uint8_t *)src;
208                         src = (const uint8_t *)src + 1;
209                         dst = (uint8_t *)dst + 1;
210                 }
211                 if (n & 0x02) {
212                         *(uint16_t *)dst = *(const uint16_t *)src;
213                         src = (const uint16_t *)src + 1;
214                         dst = (uint16_t *)dst + 1;
215                 }
216                 if (n & 0x04) {
217                         *(uint32_t *)dst = *(const uint32_t *)src;
218                         src = (const uint32_t *)src + 1;
219                         dst = (uint32_t *)dst + 1;
220                 }
221                 if (n & 0x08) {
222                         *(uint64_t *)dst = *(const uint64_t *)src;
223                 }
224                 return ret;
225         }
226
227         /**
228          * Fast way when copy size doesn't exceed 512 bytes
229          */
230         if (n <= 32) {
231                 rte_mov16((uint8_t *)dst, (const uint8_t *)src);
232                 rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
233                 return ret;
234         }
235         if (n <= 64) {
236                 rte_mov32((uint8_t *)dst, (const uint8_t *)src);
237                 rte_mov32((uint8_t *)dst - 32 + n, (const uint8_t *)src - 32 + n);
238                 return ret;
239         }
240         if (n <= 512) {
241                 if (n >= 256) {
242                         n -= 256;
243                         rte_mov256((uint8_t *)dst, (const uint8_t *)src);
244                         src = (const uint8_t *)src + 256;
245                         dst = (uint8_t *)dst + 256;
246                 }
247                 if (n >= 128) {
248                         n -= 128;
249                         rte_mov128((uint8_t *)dst, (const uint8_t *)src);
250                         src = (const uint8_t *)src + 128;
251                         dst = (uint8_t *)dst + 128;
252                 }
253                 if (n >= 64) {
254                         n -= 64;
255                         rte_mov64((uint8_t *)dst, (const uint8_t *)src);
256                         src = (const uint8_t *)src + 64;
257                         dst = (uint8_t *)dst + 64;
258                 }
259 COPY_BLOCK_64_BACK31:
260                 if (n > 32) {
261                         rte_mov32((uint8_t *)dst, (const uint8_t *)src);
262                         rte_mov32((uint8_t *)dst - 32 + n, (const uint8_t *)src - 32 + n);
263                         return ret;
264                 }
265                 if (n > 0) {
266                         rte_mov32((uint8_t *)dst - 32 + n, (const uint8_t *)src - 32 + n);
267                 }
268                 return ret;
269         }
270
271         /**
272          * Make store aligned when copy size exceeds 512 bytes
273          */
274         dstofss = 32 - (int)((long long)(void *)dst & 0x1F);
275         n -= dstofss;
276         rte_mov32((uint8_t *)dst, (const uint8_t *)src);
277         src = (const uint8_t *)src + dstofss;
278         dst = (uint8_t *)dst + dstofss;
279
280         /**
281          * Copy 256-byte blocks.
282          * Use copy block function for better instruction order control,
283          * which is important when load is unaligned.
284          */
285         rte_mov256blocks((uint8_t *)dst, (const uint8_t *)src, n);
286         bits = n;
287         n = n & 255;
288         bits -= n;
289         src = (const uint8_t *)src + bits;
290         dst = (uint8_t *)dst + bits;
291
292         /**
293          * Copy 64-byte blocks.
294          * Use copy block function for better instruction order control,
295          * which is important when load is unaligned.
296          */
297         if (n >= 64) {
298                 rte_mov64blocks((uint8_t *)dst, (const uint8_t *)src, n);
299                 bits = n;
300                 n = n & 63;
301                 bits -= n;
302                 src = (const uint8_t *)src + bits;
303                 dst = (uint8_t *)dst + bits;
304         }
305
306         /**
307          * Copy whatever left
308          */
309         goto COPY_BLOCK_64_BACK31;
310 }
311
312 #else /* RTE_MACHINE_CPUFLAG_AVX2 */
313
314 /**
315  * SSE & AVX implementation below
316  */
317
318 /**
319  * Copy 16 bytes from one location to another,
320  * locations should not overlap.
321  */
322 static inline void
323 rte_mov16(uint8_t *dst, const uint8_t *src)
324 {
325         __m128i xmm0;
326
327         xmm0 = _mm_loadu_si128((const __m128i *)(const __m128i *)src);
328         _mm_storeu_si128((__m128i *)dst, xmm0);
329 }
330
331 /**
332  * Copy 32 bytes from one location to another,
333  * locations should not overlap.
334  */
335 static inline void
336 rte_mov32(uint8_t *dst, const uint8_t *src)
337 {
338         rte_mov16((uint8_t *)dst + 0 * 16, (const uint8_t *)src + 0 * 16);
339         rte_mov16((uint8_t *)dst + 1 * 16, (const uint8_t *)src + 1 * 16);
340 }
341
342 /**
343  * Copy 64 bytes from one location to another,
344  * locations should not overlap.
345  */
346 static inline void
347 rte_mov64(uint8_t *dst, const uint8_t *src)
348 {
349         rte_mov16((uint8_t *)dst + 0 * 16, (const uint8_t *)src + 0 * 16);
350         rte_mov16((uint8_t *)dst + 1 * 16, (const uint8_t *)src + 1 * 16);
351         rte_mov16((uint8_t *)dst + 2 * 16, (const uint8_t *)src + 2 * 16);
352         rte_mov16((uint8_t *)dst + 3 * 16, (const uint8_t *)src + 3 * 16);
353 }
354
355 /**
356  * Copy 128 bytes from one location to another,
357  * locations should not overlap.
358  */
359 static inline void
360 rte_mov128(uint8_t *dst, const uint8_t *src)
361 {
362         rte_mov16((uint8_t *)dst + 0 * 16, (const uint8_t *)src + 0 * 16);
363         rte_mov16((uint8_t *)dst + 1 * 16, (const uint8_t *)src + 1 * 16);
364         rte_mov16((uint8_t *)dst + 2 * 16, (const uint8_t *)src + 2 * 16);
365         rte_mov16((uint8_t *)dst + 3 * 16, (const uint8_t *)src + 3 * 16);
366         rte_mov16((uint8_t *)dst + 4 * 16, (const uint8_t *)src + 4 * 16);
367         rte_mov16((uint8_t *)dst + 5 * 16, (const uint8_t *)src + 5 * 16);
368         rte_mov16((uint8_t *)dst + 6 * 16, (const uint8_t *)src + 6 * 16);
369         rte_mov16((uint8_t *)dst + 7 * 16, (const uint8_t *)src + 7 * 16);
370 }
371
372 /**
373  * Copy 256 bytes from one location to another,
374  * locations should not overlap.
375  */
376 static inline void
377 rte_mov256(uint8_t *dst, const uint8_t *src)
378 {
379         rte_mov16((uint8_t *)dst + 0 * 16, (const uint8_t *)src + 0 * 16);
380         rte_mov16((uint8_t *)dst + 1 * 16, (const uint8_t *)src + 1 * 16);
381         rte_mov16((uint8_t *)dst + 2 * 16, (const uint8_t *)src + 2 * 16);
382         rte_mov16((uint8_t *)dst + 3 * 16, (const uint8_t *)src + 3 * 16);
383         rte_mov16((uint8_t *)dst + 4 * 16, (const uint8_t *)src + 4 * 16);
384         rte_mov16((uint8_t *)dst + 5 * 16, (const uint8_t *)src + 5 * 16);
385         rte_mov16((uint8_t *)dst + 6 * 16, (const uint8_t *)src + 6 * 16);
386         rte_mov16((uint8_t *)dst + 7 * 16, (const uint8_t *)src + 7 * 16);
387         rte_mov16((uint8_t *)dst + 8 * 16, (const uint8_t *)src + 8 * 16);
388         rte_mov16((uint8_t *)dst + 9 * 16, (const uint8_t *)src + 9 * 16);
389         rte_mov16((uint8_t *)dst + 10 * 16, (const uint8_t *)src + 10 * 16);
390         rte_mov16((uint8_t *)dst + 11 * 16, (const uint8_t *)src + 11 * 16);
391         rte_mov16((uint8_t *)dst + 12 * 16, (const uint8_t *)src + 12 * 16);
392         rte_mov16((uint8_t *)dst + 13 * 16, (const uint8_t *)src + 13 * 16);
393         rte_mov16((uint8_t *)dst + 14 * 16, (const uint8_t *)src + 14 * 16);
394         rte_mov16((uint8_t *)dst + 15 * 16, (const uint8_t *)src + 15 * 16);
395 }
396
397 /**
398  * Macro for copying unaligned block from one location to another with constant load offset,
399  * 47 bytes leftover maximum,
400  * locations should not overlap.
401  * Requirements:
402  * - Store is aligned
403  * - Load offset is <offset>, which must be immediate value within [1, 15]
404  * - For <src>, make sure <offset> bit backwards & <16 - offset> bit forwards are available for loading
405  * - <dst>, <src>, <len> must be variables
406  * - __m128i <xmm0> ~ <xmm8> must be pre-defined
407  */
408 #define MOVEUNALIGNED_LEFT47_IMM(dst, src, len, offset)                                                     \
409 ({                                                                                                          \
410     int tmp;                                                                                                \
411     while (len >= 128 + 16 - offset) {                                                                      \
412         xmm0 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 0 * 16));                  \
413         len -= 128;                                                                                         \
414         xmm1 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 1 * 16));                  \
415         xmm2 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 2 * 16));                  \
416         xmm3 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 3 * 16));                  \
417         xmm4 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 4 * 16));                  \
418         xmm5 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 5 * 16));                  \
419         xmm6 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 6 * 16));                  \
420         xmm7 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 7 * 16));                  \
421         xmm8 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 8 * 16));                  \
422         src = (const uint8_t *)src + 128;                                                                   \
423         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 0 * 16), _mm_alignr_epi8(xmm1, xmm0, offset));        \
424         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 1 * 16), _mm_alignr_epi8(xmm2, xmm1, offset));        \
425         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 2 * 16), _mm_alignr_epi8(xmm3, xmm2, offset));        \
426         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 3 * 16), _mm_alignr_epi8(xmm4, xmm3, offset));        \
427         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 4 * 16), _mm_alignr_epi8(xmm5, xmm4, offset));        \
428         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 5 * 16), _mm_alignr_epi8(xmm6, xmm5, offset));        \
429         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 6 * 16), _mm_alignr_epi8(xmm7, xmm6, offset));        \
430         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 7 * 16), _mm_alignr_epi8(xmm8, xmm7, offset));        \
431         dst = (uint8_t *)dst + 128;                                                                         \
432     }                                                                                                       \
433     tmp = len;                                                                                              \
434     len = ((len - 16 + offset) & 127) + 16 - offset;                                                        \
435     tmp -= len;                                                                                             \
436     src = (const uint8_t *)src + tmp;                                                                       \
437     dst = (uint8_t *)dst + tmp;                                                                             \
438     if (len >= 32 + 16 - offset) {                                                                          \
439         while (len >= 32 + 16 - offset) {                                                                   \
440             xmm0 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 0 * 16));              \
441             len -= 32;                                                                                      \
442             xmm1 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 1 * 16));              \
443             xmm2 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 2 * 16));              \
444             src = (const uint8_t *)src + 32;                                                                \
445             _mm_storeu_si128((__m128i *)((uint8_t *)dst + 0 * 16), _mm_alignr_epi8(xmm1, xmm0, offset));    \
446             _mm_storeu_si128((__m128i *)((uint8_t *)dst + 1 * 16), _mm_alignr_epi8(xmm2, xmm1, offset));    \
447             dst = (uint8_t *)dst + 32;                                                                      \
448         }                                                                                                   \
449         tmp = len;                                                                                          \
450         len = ((len - 16 + offset) & 31) + 16 - offset;                                                     \
451         tmp -= len;                                                                                         \
452         src = (const uint8_t *)src + tmp;                                                                   \
453         dst = (uint8_t *)dst + tmp;                                                                         \
454     }                                                                                                       \
455 })
456
457 /**
458  * Macro for copying unaligned block from one location to another,
459  * 47 bytes leftover maximum,
460  * locations should not overlap.
461  * Use switch here because the aligning instruction requires immediate value for shift count.
462  * Requirements:
463  * - Store is aligned
464  * - Load offset is <offset>, which must be within [1, 15]
465  * - For <src>, make sure <offset> bit backwards & <16 - offset> bit forwards are available for loading
466  * - <dst>, <src>, <len> must be variables
467  * - __m128i <xmm0> ~ <xmm8> used in MOVEUNALIGNED_LEFT47_IMM must be pre-defined
468  */
469 #define MOVEUNALIGNED_LEFT47(dst, src, len, offset)                   \
470 ({                                                                    \
471     switch (offset) {                                                 \
472     case 0x01: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x01); break;    \
473     case 0x02: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x02); break;    \
474     case 0x03: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x03); break;    \
475     case 0x04: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x04); break;    \
476     case 0x05: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x05); break;    \
477     case 0x06: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x06); break;    \
478     case 0x07: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x07); break;    \
479     case 0x08: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x08); break;    \
480     case 0x09: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x09); break;    \
481     case 0x0A: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0A); break;    \
482     case 0x0B: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0B); break;    \
483     case 0x0C: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0C); break;    \
484     case 0x0D: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0D); break;    \
485     case 0x0E: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0E); break;    \
486     case 0x0F: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0F); break;    \
487     default:;                                                         \
488     }                                                                 \
489 })
490
491 static inline void *
492 rte_memcpy(void *dst, const void *src, size_t n)
493 {
494         __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8;
495         void *ret = dst;
496         int dstofss;
497         int srcofs;
498
499         /**
500          * Copy less than 16 bytes
501          */
502         if (n < 16) {
503                 if (n & 0x01) {
504                         *(uint8_t *)dst = *(const uint8_t *)src;
505                         src = (const uint8_t *)src + 1;
506                         dst = (uint8_t *)dst + 1;
507                 }
508                 if (n & 0x02) {
509                         *(uint16_t *)dst = *(const uint16_t *)src;
510                         src = (const uint16_t *)src + 1;
511                         dst = (uint16_t *)dst + 1;
512                 }
513                 if (n & 0x04) {
514                         *(uint32_t *)dst = *(const uint32_t *)src;
515                         src = (const uint32_t *)src + 1;
516                         dst = (uint32_t *)dst + 1;
517                 }
518                 if (n & 0x08) {
519                         *(uint64_t *)dst = *(const uint64_t *)src;
520                 }
521                 return ret;
522         }
523
524         /**
525          * Fast way when copy size doesn't exceed 512 bytes
526          */
527         if (n <= 32) {
528                 rte_mov16((uint8_t *)dst, (const uint8_t *)src);
529                 rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
530                 return ret;
531         }
532         if (n <= 48) {
533                 rte_mov32((uint8_t *)dst, (const uint8_t *)src);
534                 rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
535                 return ret;
536         }
537         if (n <= 64) {
538                 rte_mov32((uint8_t *)dst, (const uint8_t *)src);
539                 rte_mov16((uint8_t *)dst + 32, (const uint8_t *)src + 32);
540                 rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
541                 return ret;
542         }
543         if (n <= 128) {
544                 goto COPY_BLOCK_128_BACK15;
545         }
546         if (n <= 512) {
547                 if (n >= 256) {
548                         n -= 256;
549                         rte_mov128((uint8_t *)dst, (const uint8_t *)src);
550                         rte_mov128((uint8_t *)dst + 128, (const uint8_t *)src + 128);
551                         src = (const uint8_t *)src + 256;
552                         dst = (uint8_t *)dst + 256;
553                 }
554 COPY_BLOCK_255_BACK15:
555                 if (n >= 128) {
556                         n -= 128;
557                         rte_mov128((uint8_t *)dst, (const uint8_t *)src);
558                         src = (const uint8_t *)src + 128;
559                         dst = (uint8_t *)dst + 128;
560                 }
561 COPY_BLOCK_128_BACK15:
562                 if (n >= 64) {
563                         n -= 64;
564                         rte_mov64((uint8_t *)dst, (const uint8_t *)src);
565                         src = (const uint8_t *)src + 64;
566                         dst = (uint8_t *)dst + 64;
567                 }
568 COPY_BLOCK_64_BACK15:
569                 if (n >= 32) {
570                         n -= 32;
571                         rte_mov32((uint8_t *)dst, (const uint8_t *)src);
572                         src = (const uint8_t *)src + 32;
573                         dst = (uint8_t *)dst + 32;
574                 }
575                 if (n > 16) {
576                         rte_mov16((uint8_t *)dst, (const uint8_t *)src);
577                         rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
578                         return ret;
579                 }
580                 if (n > 0) {
581                         rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
582                 }
583                 return ret;
584         }
585
586         /**
587          * Make store aligned when copy size exceeds 512 bytes,
588          * and make sure the first 15 bytes are copied, because
589          * unaligned copy functions require up to 15 bytes
590          * backwards access.
591          */
592         dstofss = 16 - (int)((long long)(void *)dst & 0x0F) + 16;
593         n -= dstofss;
594         rte_mov32((uint8_t *)dst, (const uint8_t *)src);
595         src = (const uint8_t *)src + dstofss;
596         dst = (uint8_t *)dst + dstofss;
597         srcofs = (int)((long long)(const void *)src & 0x0F);
598
599         /**
600          * For aligned copy
601          */
602         if (srcofs == 0) {
603                 /**
604                  * Copy 256-byte blocks
605                  */
606                 for (; n >= 256; n -= 256) {
607                         rte_mov256((uint8_t *)dst, (const uint8_t *)src);
608                         dst = (uint8_t *)dst + 256;
609                         src = (const uint8_t *)src + 256;
610                 }
611
612                 /**
613                  * Copy whatever left
614                  */
615                 goto COPY_BLOCK_255_BACK15;
616         }
617
618         /**
619          * For copy with unaligned load
620          */
621         MOVEUNALIGNED_LEFT47(dst, src, n, srcofs);
622
623         /**
624          * Copy whatever left
625          */
626         goto COPY_BLOCK_64_BACK15;
627 }
628
629 #endif /* RTE_MACHINE_CPUFLAG_AVX2 */
630
631 #ifdef __cplusplus
632 }
633 #endif
634
635 #endif /* _RTE_MEMCPY_X86_64_H_ */