b9785e85ee43843aa1670d744133de0cf8003780
[deb_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/AVX512 implementation of memcpy().
41  */
42
43 #include <stdio.h>
44 #include <stdint.h>
45 #include <string.h>
46 #include <rte_vect.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_AVX512F
71
72 #define ALIGNMENT_MASK 0x3F
73
74 /**
75  * AVX512 implementation below
76  */
77
78 /**
79  * Copy 16 bytes from one location to another,
80  * locations should not overlap.
81  */
82 static inline void
83 rte_mov16(uint8_t *dst, const uint8_t *src)
84 {
85         __m128i xmm0;
86
87         xmm0 = _mm_loadu_si128((const __m128i *)src);
88         _mm_storeu_si128((__m128i *)dst, xmm0);
89 }
90
91 /**
92  * Copy 32 bytes from one location to another,
93  * locations should not overlap.
94  */
95 static inline void
96 rte_mov32(uint8_t *dst, const uint8_t *src)
97 {
98         __m256i ymm0;
99
100         ymm0 = _mm256_loadu_si256((const __m256i *)src);
101         _mm256_storeu_si256((__m256i *)dst, ymm0);
102 }
103
104 /**
105  * Copy 64 bytes from one location to another,
106  * locations should not overlap.
107  */
108 static inline void
109 rte_mov64(uint8_t *dst, const uint8_t *src)
110 {
111         __m512i zmm0;
112
113         zmm0 = _mm512_loadu_si512((const void *)src);
114         _mm512_storeu_si512((void *)dst, zmm0);
115 }
116
117 /**
118  * Copy 128 bytes from one location to another,
119  * locations should not overlap.
120  */
121 static inline void
122 rte_mov128(uint8_t *dst, const uint8_t *src)
123 {
124         rte_mov64(dst + 0 * 64, src + 0 * 64);
125         rte_mov64(dst + 1 * 64, src + 1 * 64);
126 }
127
128 /**
129  * Copy 256 bytes from one location to another,
130  * locations should not overlap.
131  */
132 static inline void
133 rte_mov256(uint8_t *dst, const uint8_t *src)
134 {
135         rte_mov64(dst + 0 * 64, src + 0 * 64);
136         rte_mov64(dst + 1 * 64, src + 1 * 64);
137         rte_mov64(dst + 2 * 64, src + 2 * 64);
138         rte_mov64(dst + 3 * 64, src + 3 * 64);
139 }
140
141 /**
142  * Copy 128-byte blocks from one location to another,
143  * locations should not overlap.
144  */
145 static inline void
146 rte_mov128blocks(uint8_t *dst, const uint8_t *src, size_t n)
147 {
148         __m512i zmm0, zmm1;
149
150         while (n >= 128) {
151                 zmm0 = _mm512_loadu_si512((const void *)(src + 0 * 64));
152                 n -= 128;
153                 zmm1 = _mm512_loadu_si512((const void *)(src + 1 * 64));
154                 src = src + 128;
155                 _mm512_storeu_si512((void *)(dst + 0 * 64), zmm0);
156                 _mm512_storeu_si512((void *)(dst + 1 * 64), zmm1);
157                 dst = dst + 128;
158         }
159 }
160
161 /**
162  * Copy 512-byte blocks from one location to another,
163  * locations should not overlap.
164  */
165 static inline void
166 rte_mov512blocks(uint8_t *dst, const uint8_t *src, size_t n)
167 {
168         __m512i zmm0, zmm1, zmm2, zmm3, zmm4, zmm5, zmm6, zmm7;
169
170         while (n >= 512) {
171                 zmm0 = _mm512_loadu_si512((const void *)(src + 0 * 64));
172                 n -= 512;
173                 zmm1 = _mm512_loadu_si512((const void *)(src + 1 * 64));
174                 zmm2 = _mm512_loadu_si512((const void *)(src + 2 * 64));
175                 zmm3 = _mm512_loadu_si512((const void *)(src + 3 * 64));
176                 zmm4 = _mm512_loadu_si512((const void *)(src + 4 * 64));
177                 zmm5 = _mm512_loadu_si512((const void *)(src + 5 * 64));
178                 zmm6 = _mm512_loadu_si512((const void *)(src + 6 * 64));
179                 zmm7 = _mm512_loadu_si512((const void *)(src + 7 * 64));
180                 src = src + 512;
181                 _mm512_storeu_si512((void *)(dst + 0 * 64), zmm0);
182                 _mm512_storeu_si512((void *)(dst + 1 * 64), zmm1);
183                 _mm512_storeu_si512((void *)(dst + 2 * 64), zmm2);
184                 _mm512_storeu_si512((void *)(dst + 3 * 64), zmm3);
185                 _mm512_storeu_si512((void *)(dst + 4 * 64), zmm4);
186                 _mm512_storeu_si512((void *)(dst + 5 * 64), zmm5);
187                 _mm512_storeu_si512((void *)(dst + 6 * 64), zmm6);
188                 _mm512_storeu_si512((void *)(dst + 7 * 64), zmm7);
189                 dst = dst + 512;
190         }
191 }
192
193 static inline void *
194 rte_memcpy_generic(void *dst, const void *src, size_t n)
195 {
196         uintptr_t dstu = (uintptr_t)dst;
197         uintptr_t srcu = (uintptr_t)src;
198         void *ret = dst;
199         size_t dstofss;
200         size_t bits;
201
202         /**
203          * Copy less than 16 bytes
204          */
205         if (n < 16) {
206                 if (n & 0x01) {
207                         *(uint8_t *)dstu = *(const uint8_t *)srcu;
208                         srcu = (uintptr_t)((const uint8_t *)srcu + 1);
209                         dstu = (uintptr_t)((uint8_t *)dstu + 1);
210                 }
211                 if (n & 0x02) {
212                         *(uint16_t *)dstu = *(const uint16_t *)srcu;
213                         srcu = (uintptr_t)((const uint16_t *)srcu + 1);
214                         dstu = (uintptr_t)((uint16_t *)dstu + 1);
215                 }
216                 if (n & 0x04) {
217                         *(uint32_t *)dstu = *(const uint32_t *)srcu;
218                         srcu = (uintptr_t)((const uint32_t *)srcu + 1);
219                         dstu = (uintptr_t)((uint32_t *)dstu + 1);
220                 }
221                 if (n & 0x08)
222                         *(uint64_t *)dstu = *(const uint64_t *)srcu;
223                 return ret;
224         }
225
226         /**
227          * Fast way when copy size doesn't exceed 512 bytes
228          */
229         if (n <= 32) {
230                 rte_mov16((uint8_t *)dst, (const uint8_t *)src);
231                 rte_mov16((uint8_t *)dst - 16 + n,
232                                   (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,
238                                   (const uint8_t *)src - 32 + n);
239                 return ret;
240         }
241         if (n <= 512) {
242                 if (n >= 256) {
243                         n -= 256;
244                         rte_mov256((uint8_t *)dst, (const uint8_t *)src);
245                         src = (const uint8_t *)src + 256;
246                         dst = (uint8_t *)dst + 256;
247                 }
248                 if (n >= 128) {
249                         n -= 128;
250                         rte_mov128((uint8_t *)dst, (const uint8_t *)src);
251                         src = (const uint8_t *)src + 128;
252                         dst = (uint8_t *)dst + 128;
253                 }
254 COPY_BLOCK_128_BACK63:
255                 if (n > 64) {
256                         rte_mov64((uint8_t *)dst, (const uint8_t *)src);
257                         rte_mov64((uint8_t *)dst - 64 + n,
258                                           (const uint8_t *)src - 64 + n);
259                         return ret;
260                 }
261                 if (n > 0)
262                         rte_mov64((uint8_t *)dst - 64 + n,
263                                           (const uint8_t *)src - 64 + n);
264                 return ret;
265         }
266
267         /**
268          * Make store aligned when copy size exceeds 512 bytes
269          */
270         dstofss = ((uintptr_t)dst & 0x3F);
271         if (dstofss > 0) {
272                 dstofss = 64 - dstofss;
273                 n -= dstofss;
274                 rte_mov64((uint8_t *)dst, (const uint8_t *)src);
275                 src = (const uint8_t *)src + dstofss;
276                 dst = (uint8_t *)dst + dstofss;
277         }
278
279         /**
280          * Copy 512-byte blocks.
281          * Use copy block function for better instruction order control,
282          * which is important when load is unaligned.
283          */
284         rte_mov512blocks((uint8_t *)dst, (const uint8_t *)src, n);
285         bits = n;
286         n = n & 511;
287         bits -= n;
288         src = (const uint8_t *)src + bits;
289         dst = (uint8_t *)dst + bits;
290
291         /**
292          * Copy 128-byte blocks.
293          * Use copy block function for better instruction order control,
294          * which is important when load is unaligned.
295          */
296         if (n >= 128) {
297                 rte_mov128blocks((uint8_t *)dst, (const uint8_t *)src, n);
298                 bits = n;
299                 n = n & 127;
300                 bits -= n;
301                 src = (const uint8_t *)src + bits;
302                 dst = (uint8_t *)dst + bits;
303         }
304
305         /**
306          * Copy whatever left
307          */
308         goto COPY_BLOCK_128_BACK63;
309 }
310
311 #elif defined RTE_MACHINE_CPUFLAG_AVX2
312
313 #define ALIGNMENT_MASK 0x1F
314
315 /**
316  * AVX2 implementation below
317  */
318
319 /**
320  * Copy 16 bytes from one location to another,
321  * locations should not overlap.
322  */
323 static inline void
324 rte_mov16(uint8_t *dst, const uint8_t *src)
325 {
326         __m128i xmm0;
327
328         xmm0 = _mm_loadu_si128((const __m128i *)src);
329         _mm_storeu_si128((__m128i *)dst, xmm0);
330 }
331
332 /**
333  * Copy 32 bytes from one location to another,
334  * locations should not overlap.
335  */
336 static inline void
337 rte_mov32(uint8_t *dst, const uint8_t *src)
338 {
339         __m256i ymm0;
340
341         ymm0 = _mm256_loadu_si256((const __m256i *)src);
342         _mm256_storeu_si256((__m256i *)dst, ymm0);
343 }
344
345 /**
346  * Copy 64 bytes from one location to another,
347  * locations should not overlap.
348  */
349 static inline void
350 rte_mov64(uint8_t *dst, const uint8_t *src)
351 {
352         rte_mov32((uint8_t *)dst + 0 * 32, (const uint8_t *)src + 0 * 32);
353         rte_mov32((uint8_t *)dst + 1 * 32, (const uint8_t *)src + 1 * 32);
354 }
355
356 /**
357  * Copy 128 bytes from one location to another,
358  * locations should not overlap.
359  */
360 static inline void
361 rte_mov128(uint8_t *dst, const uint8_t *src)
362 {
363         rte_mov32((uint8_t *)dst + 0 * 32, (const uint8_t *)src + 0 * 32);
364         rte_mov32((uint8_t *)dst + 1 * 32, (const uint8_t *)src + 1 * 32);
365         rte_mov32((uint8_t *)dst + 2 * 32, (const uint8_t *)src + 2 * 32);
366         rte_mov32((uint8_t *)dst + 3 * 32, (const uint8_t *)src + 3 * 32);
367 }
368
369 /**
370  * Copy 128-byte blocks from one location to another,
371  * locations should not overlap.
372  */
373 static inline void
374 rte_mov128blocks(uint8_t *dst, const uint8_t *src, size_t n)
375 {
376         __m256i ymm0, ymm1, ymm2, ymm3;
377
378         while (n >= 128) {
379                 ymm0 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 0 * 32));
380                 n -= 128;
381                 ymm1 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 1 * 32));
382                 ymm2 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 2 * 32));
383                 ymm3 = _mm256_loadu_si256((const __m256i *)((const uint8_t *)src + 3 * 32));
384                 src = (const uint8_t *)src + 128;
385                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 0 * 32), ymm0);
386                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 1 * 32), ymm1);
387                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 2 * 32), ymm2);
388                 _mm256_storeu_si256((__m256i *)((uint8_t *)dst + 3 * 32), ymm3);
389                 dst = (uint8_t *)dst + 128;
390         }
391 }
392
393 static inline void *
394 rte_memcpy_generic(void *dst, const void *src, size_t n)
395 {
396         uintptr_t dstu = (uintptr_t)dst;
397         uintptr_t srcu = (uintptr_t)src;
398         void *ret = dst;
399         size_t dstofss;
400         size_t bits;
401
402         /**
403          * Copy less than 16 bytes
404          */
405         if (n < 16) {
406                 if (n & 0x01) {
407                         *(uint8_t *)dstu = *(const uint8_t *)srcu;
408                         srcu = (uintptr_t)((const uint8_t *)srcu + 1);
409                         dstu = (uintptr_t)((uint8_t *)dstu + 1);
410                 }
411                 if (n & 0x02) {
412                         *(uint16_t *)dstu = *(const uint16_t *)srcu;
413                         srcu = (uintptr_t)((const uint16_t *)srcu + 1);
414                         dstu = (uintptr_t)((uint16_t *)dstu + 1);
415                 }
416                 if (n & 0x04) {
417                         *(uint32_t *)dstu = *(const uint32_t *)srcu;
418                         srcu = (uintptr_t)((const uint32_t *)srcu + 1);
419                         dstu = (uintptr_t)((uint32_t *)dstu + 1);
420                 }
421                 if (n & 0x08) {
422                         *(uint64_t *)dstu = *(const uint64_t *)srcu;
423                 }
424                 return ret;
425         }
426
427         /**
428          * Fast way when copy size doesn't exceed 256 bytes
429          */
430         if (n <= 32) {
431                 rte_mov16((uint8_t *)dst, (const uint8_t *)src);
432                 rte_mov16((uint8_t *)dst - 16 + n,
433                                 (const uint8_t *)src - 16 + n);
434                 return ret;
435         }
436         if (n <= 48) {
437                 rte_mov16((uint8_t *)dst, (const uint8_t *)src);
438                 rte_mov16((uint8_t *)dst + 16, (const uint8_t *)src + 16);
439                 rte_mov16((uint8_t *)dst - 16 + n,
440                                 (const uint8_t *)src - 16 + n);
441                 return ret;
442         }
443         if (n <= 64) {
444                 rte_mov32((uint8_t *)dst, (const uint8_t *)src);
445                 rte_mov32((uint8_t *)dst - 32 + n,
446                                 (const uint8_t *)src - 32 + n);
447                 return ret;
448         }
449         if (n <= 256) {
450                 if (n >= 128) {
451                         n -= 128;
452                         rte_mov128((uint8_t *)dst, (const uint8_t *)src);
453                         src = (const uint8_t *)src + 128;
454                         dst = (uint8_t *)dst + 128;
455                 }
456 COPY_BLOCK_128_BACK31:
457                 if (n >= 64) {
458                         n -= 64;
459                         rte_mov64((uint8_t *)dst, (const uint8_t *)src);
460                         src = (const uint8_t *)src + 64;
461                         dst = (uint8_t *)dst + 64;
462                 }
463                 if (n > 32) {
464                         rte_mov32((uint8_t *)dst, (const uint8_t *)src);
465                         rte_mov32((uint8_t *)dst - 32 + n,
466                                         (const uint8_t *)src - 32 + n);
467                         return ret;
468                 }
469                 if (n > 0) {
470                         rte_mov32((uint8_t *)dst - 32 + n,
471                                         (const uint8_t *)src - 32 + n);
472                 }
473                 return ret;
474         }
475
476         /**
477          * Make store aligned when copy size exceeds 256 bytes
478          */
479         dstofss = (uintptr_t)dst & 0x1F;
480         if (dstofss > 0) {
481                 dstofss = 32 - dstofss;
482                 n -= dstofss;
483                 rte_mov32((uint8_t *)dst, (const uint8_t *)src);
484                 src = (const uint8_t *)src + dstofss;
485                 dst = (uint8_t *)dst + dstofss;
486         }
487
488         /**
489          * Copy 128-byte blocks
490          */
491         rte_mov128blocks((uint8_t *)dst, (const uint8_t *)src, n);
492         bits = n;
493         n = n & 127;
494         bits -= n;
495         src = (const uint8_t *)src + bits;
496         dst = (uint8_t *)dst + bits;
497
498         /**
499          * Copy whatever left
500          */
501         goto COPY_BLOCK_128_BACK31;
502 }
503
504 #else /* RTE_MACHINE_CPUFLAG */
505
506 #define ALIGNMENT_MASK 0x0F
507
508 /**
509  * SSE & AVX implementation below
510  */
511
512 /**
513  * Copy 16 bytes from one location to another,
514  * locations should not overlap.
515  */
516 static inline void
517 rte_mov16(uint8_t *dst, const uint8_t *src)
518 {
519         __m128i xmm0;
520
521         xmm0 = _mm_loadu_si128((const __m128i *)(const __m128i *)src);
522         _mm_storeu_si128((__m128i *)dst, xmm0);
523 }
524
525 /**
526  * Copy 32 bytes from one location to another,
527  * locations should not overlap.
528  */
529 static inline void
530 rte_mov32(uint8_t *dst, const uint8_t *src)
531 {
532         rte_mov16((uint8_t *)dst + 0 * 16, (const uint8_t *)src + 0 * 16);
533         rte_mov16((uint8_t *)dst + 1 * 16, (const uint8_t *)src + 1 * 16);
534 }
535
536 /**
537  * Copy 64 bytes from one location to another,
538  * locations should not overlap.
539  */
540 static inline void
541 rte_mov64(uint8_t *dst, const uint8_t *src)
542 {
543         rte_mov16((uint8_t *)dst + 0 * 16, (const uint8_t *)src + 0 * 16);
544         rte_mov16((uint8_t *)dst + 1 * 16, (const uint8_t *)src + 1 * 16);
545         rte_mov16((uint8_t *)dst + 2 * 16, (const uint8_t *)src + 2 * 16);
546         rte_mov16((uint8_t *)dst + 3 * 16, (const uint8_t *)src + 3 * 16);
547 }
548
549 /**
550  * Copy 128 bytes from one location to another,
551  * locations should not overlap.
552  */
553 static inline void
554 rte_mov128(uint8_t *dst, const uint8_t *src)
555 {
556         rte_mov16((uint8_t *)dst + 0 * 16, (const uint8_t *)src + 0 * 16);
557         rte_mov16((uint8_t *)dst + 1 * 16, (const uint8_t *)src + 1 * 16);
558         rte_mov16((uint8_t *)dst + 2 * 16, (const uint8_t *)src + 2 * 16);
559         rte_mov16((uint8_t *)dst + 3 * 16, (const uint8_t *)src + 3 * 16);
560         rte_mov16((uint8_t *)dst + 4 * 16, (const uint8_t *)src + 4 * 16);
561         rte_mov16((uint8_t *)dst + 5 * 16, (const uint8_t *)src + 5 * 16);
562         rte_mov16((uint8_t *)dst + 6 * 16, (const uint8_t *)src + 6 * 16);
563         rte_mov16((uint8_t *)dst + 7 * 16, (const uint8_t *)src + 7 * 16);
564 }
565
566 /**
567  * Copy 256 bytes from one location to another,
568  * locations should not overlap.
569  */
570 static inline void
571 rte_mov256(uint8_t *dst, const uint8_t *src)
572 {
573         rte_mov16((uint8_t *)dst + 0 * 16, (const uint8_t *)src + 0 * 16);
574         rte_mov16((uint8_t *)dst + 1 * 16, (const uint8_t *)src + 1 * 16);
575         rte_mov16((uint8_t *)dst + 2 * 16, (const uint8_t *)src + 2 * 16);
576         rte_mov16((uint8_t *)dst + 3 * 16, (const uint8_t *)src + 3 * 16);
577         rte_mov16((uint8_t *)dst + 4 * 16, (const uint8_t *)src + 4 * 16);
578         rte_mov16((uint8_t *)dst + 5 * 16, (const uint8_t *)src + 5 * 16);
579         rte_mov16((uint8_t *)dst + 6 * 16, (const uint8_t *)src + 6 * 16);
580         rte_mov16((uint8_t *)dst + 7 * 16, (const uint8_t *)src + 7 * 16);
581         rte_mov16((uint8_t *)dst + 8 * 16, (const uint8_t *)src + 8 * 16);
582         rte_mov16((uint8_t *)dst + 9 * 16, (const uint8_t *)src + 9 * 16);
583         rte_mov16((uint8_t *)dst + 10 * 16, (const uint8_t *)src + 10 * 16);
584         rte_mov16((uint8_t *)dst + 11 * 16, (const uint8_t *)src + 11 * 16);
585         rte_mov16((uint8_t *)dst + 12 * 16, (const uint8_t *)src + 12 * 16);
586         rte_mov16((uint8_t *)dst + 13 * 16, (const uint8_t *)src + 13 * 16);
587         rte_mov16((uint8_t *)dst + 14 * 16, (const uint8_t *)src + 14 * 16);
588         rte_mov16((uint8_t *)dst + 15 * 16, (const uint8_t *)src + 15 * 16);
589 }
590
591 /**
592  * Macro for copying unaligned block from one location to another with constant load offset,
593  * 47 bytes leftover maximum,
594  * locations should not overlap.
595  * Requirements:
596  * - Store is aligned
597  * - Load offset is <offset>, which must be immediate value within [1, 15]
598  * - For <src>, make sure <offset> bit backwards & <16 - offset> bit forwards are available for loading
599  * - <dst>, <src>, <len> must be variables
600  * - __m128i <xmm0> ~ <xmm8> must be pre-defined
601  */
602 #define MOVEUNALIGNED_LEFT47_IMM(dst, src, len, offset)                                                     \
603 __extension__ ({                                                                                            \
604     int tmp;                                                                                                \
605     while (len >= 128 + 16 - offset) {                                                                      \
606         xmm0 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 0 * 16));                  \
607         len -= 128;                                                                                         \
608         xmm1 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 1 * 16));                  \
609         xmm2 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 2 * 16));                  \
610         xmm3 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 3 * 16));                  \
611         xmm4 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 4 * 16));                  \
612         xmm5 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 5 * 16));                  \
613         xmm6 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 6 * 16));                  \
614         xmm7 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 7 * 16));                  \
615         xmm8 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 8 * 16));                  \
616         src = (const uint8_t *)src + 128;                                                                   \
617         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 0 * 16), _mm_alignr_epi8(xmm1, xmm0, offset));        \
618         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 1 * 16), _mm_alignr_epi8(xmm2, xmm1, offset));        \
619         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 2 * 16), _mm_alignr_epi8(xmm3, xmm2, offset));        \
620         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 3 * 16), _mm_alignr_epi8(xmm4, xmm3, offset));        \
621         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 4 * 16), _mm_alignr_epi8(xmm5, xmm4, offset));        \
622         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 5 * 16), _mm_alignr_epi8(xmm6, xmm5, offset));        \
623         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 6 * 16), _mm_alignr_epi8(xmm7, xmm6, offset));        \
624         _mm_storeu_si128((__m128i *)((uint8_t *)dst + 7 * 16), _mm_alignr_epi8(xmm8, xmm7, offset));        \
625         dst = (uint8_t *)dst + 128;                                                                         \
626     }                                                                                                       \
627     tmp = len;                                                                                              \
628     len = ((len - 16 + offset) & 127) + 16 - offset;                                                        \
629     tmp -= len;                                                                                             \
630     src = (const uint8_t *)src + tmp;                                                                       \
631     dst = (uint8_t *)dst + tmp;                                                                             \
632     if (len >= 32 + 16 - offset) {                                                                          \
633         while (len >= 32 + 16 - offset) {                                                                   \
634             xmm0 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 0 * 16));              \
635             len -= 32;                                                                                      \
636             xmm1 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 1 * 16));              \
637             xmm2 = _mm_loadu_si128((const __m128i *)((const uint8_t *)src - offset + 2 * 16));              \
638             src = (const uint8_t *)src + 32;                                                                \
639             _mm_storeu_si128((__m128i *)((uint8_t *)dst + 0 * 16), _mm_alignr_epi8(xmm1, xmm0, offset));    \
640             _mm_storeu_si128((__m128i *)((uint8_t *)dst + 1 * 16), _mm_alignr_epi8(xmm2, xmm1, offset));    \
641             dst = (uint8_t *)dst + 32;                                                                      \
642         }                                                                                                   \
643         tmp = len;                                                                                          \
644         len = ((len - 16 + offset) & 31) + 16 - offset;                                                     \
645         tmp -= len;                                                                                         \
646         src = (const uint8_t *)src + tmp;                                                                   \
647         dst = (uint8_t *)dst + tmp;                                                                         \
648     }                                                                                                       \
649 })
650
651 /**
652  * Macro for copying unaligned block from one location to another,
653  * 47 bytes leftover maximum,
654  * locations should not overlap.
655  * Use switch here because the aligning instruction requires immediate value for shift count.
656  * Requirements:
657  * - Store is aligned
658  * - Load offset is <offset>, which must be within [1, 15]
659  * - For <src>, make sure <offset> bit backwards & <16 - offset> bit forwards are available for loading
660  * - <dst>, <src>, <len> must be variables
661  * - __m128i <xmm0> ~ <xmm8> used in MOVEUNALIGNED_LEFT47_IMM must be pre-defined
662  */
663 #define MOVEUNALIGNED_LEFT47(dst, src, len, offset)                   \
664 __extension__ ({                                                      \
665     switch (offset) {                                                 \
666     case 0x01: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x01); break;    \
667     case 0x02: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x02); break;    \
668     case 0x03: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x03); break;    \
669     case 0x04: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x04); break;    \
670     case 0x05: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x05); break;    \
671     case 0x06: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x06); break;    \
672     case 0x07: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x07); break;    \
673     case 0x08: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x08); break;    \
674     case 0x09: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x09); break;    \
675     case 0x0A: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0A); break;    \
676     case 0x0B: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0B); break;    \
677     case 0x0C: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0C); break;    \
678     case 0x0D: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0D); break;    \
679     case 0x0E: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0E); break;    \
680     case 0x0F: MOVEUNALIGNED_LEFT47_IMM(dst, src, n, 0x0F); break;    \
681     default:;                                                         \
682     }                                                                 \
683 })
684
685 static inline void *
686 rte_memcpy_generic(void *dst, const void *src, size_t n)
687 {
688         __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8;
689         uintptr_t dstu = (uintptr_t)dst;
690         uintptr_t srcu = (uintptr_t)src;
691         void *ret = dst;
692         size_t dstofss;
693         size_t srcofs;
694
695         /**
696          * Copy less than 16 bytes
697          */
698         if (n < 16) {
699                 if (n & 0x01) {
700                         *(uint8_t *)dstu = *(const uint8_t *)srcu;
701                         srcu = (uintptr_t)((const uint8_t *)srcu + 1);
702                         dstu = (uintptr_t)((uint8_t *)dstu + 1);
703                 }
704                 if (n & 0x02) {
705                         *(uint16_t *)dstu = *(const uint16_t *)srcu;
706                         srcu = (uintptr_t)((const uint16_t *)srcu + 1);
707                         dstu = (uintptr_t)((uint16_t *)dstu + 1);
708                 }
709                 if (n & 0x04) {
710                         *(uint32_t *)dstu = *(const uint32_t *)srcu;
711                         srcu = (uintptr_t)((const uint32_t *)srcu + 1);
712                         dstu = (uintptr_t)((uint32_t *)dstu + 1);
713                 }
714                 if (n & 0x08) {
715                         *(uint64_t *)dstu = *(const uint64_t *)srcu;
716                 }
717                 return ret;
718         }
719
720         /**
721          * Fast way when copy size doesn't exceed 512 bytes
722          */
723         if (n <= 32) {
724                 rte_mov16((uint8_t *)dst, (const uint8_t *)src);
725                 rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
726                 return ret;
727         }
728         if (n <= 48) {
729                 rte_mov32((uint8_t *)dst, (const uint8_t *)src);
730                 rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
731                 return ret;
732         }
733         if (n <= 64) {
734                 rte_mov32((uint8_t *)dst, (const uint8_t *)src);
735                 rte_mov16((uint8_t *)dst + 32, (const uint8_t *)src + 32);
736                 rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
737                 return ret;
738         }
739         if (n <= 128) {
740                 goto COPY_BLOCK_128_BACK15;
741         }
742         if (n <= 512) {
743                 if (n >= 256) {
744                         n -= 256;
745                         rte_mov128((uint8_t *)dst, (const uint8_t *)src);
746                         rte_mov128((uint8_t *)dst + 128, (const uint8_t *)src + 128);
747                         src = (const uint8_t *)src + 256;
748                         dst = (uint8_t *)dst + 256;
749                 }
750 COPY_BLOCK_255_BACK15:
751                 if (n >= 128) {
752                         n -= 128;
753                         rte_mov128((uint8_t *)dst, (const uint8_t *)src);
754                         src = (const uint8_t *)src + 128;
755                         dst = (uint8_t *)dst + 128;
756                 }
757 COPY_BLOCK_128_BACK15:
758                 if (n >= 64) {
759                         n -= 64;
760                         rte_mov64((uint8_t *)dst, (const uint8_t *)src);
761                         src = (const uint8_t *)src + 64;
762                         dst = (uint8_t *)dst + 64;
763                 }
764 COPY_BLOCK_64_BACK15:
765                 if (n >= 32) {
766                         n -= 32;
767                         rte_mov32((uint8_t *)dst, (const uint8_t *)src);
768                         src = (const uint8_t *)src + 32;
769                         dst = (uint8_t *)dst + 32;
770                 }
771                 if (n > 16) {
772                         rte_mov16((uint8_t *)dst, (const uint8_t *)src);
773                         rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
774                         return ret;
775                 }
776                 if (n > 0) {
777                         rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
778                 }
779                 return ret;
780         }
781
782         /**
783          * Make store aligned when copy size exceeds 512 bytes,
784          * and make sure the first 15 bytes are copied, because
785          * unaligned copy functions require up to 15 bytes
786          * backwards access.
787          */
788         dstofss = (uintptr_t)dst & 0x0F;
789         if (dstofss > 0) {
790                 dstofss = 16 - dstofss + 16;
791                 n -= dstofss;
792                 rte_mov32((uint8_t *)dst, (const uint8_t *)src);
793                 src = (const uint8_t *)src + dstofss;
794                 dst = (uint8_t *)dst + dstofss;
795         }
796         srcofs = ((uintptr_t)src & 0x0F);
797
798         /**
799          * For aligned copy
800          */
801         if (srcofs == 0) {
802                 /**
803                  * Copy 256-byte blocks
804                  */
805                 for (; n >= 256; n -= 256) {
806                         rte_mov256((uint8_t *)dst, (const uint8_t *)src);
807                         dst = (uint8_t *)dst + 256;
808                         src = (const uint8_t *)src + 256;
809                 }
810
811                 /**
812                  * Copy whatever left
813                  */
814                 goto COPY_BLOCK_255_BACK15;
815         }
816
817         /**
818          * For copy with unaligned load
819          */
820         MOVEUNALIGNED_LEFT47(dst, src, n, srcofs);
821
822         /**
823          * Copy whatever left
824          */
825         goto COPY_BLOCK_64_BACK15;
826 }
827
828 #endif /* RTE_MACHINE_CPUFLAG */
829
830 static inline void *
831 rte_memcpy_aligned(void *dst, const void *src, size_t n)
832 {
833         void *ret = dst;
834
835         /* Copy size <= 16 bytes */
836         if (n < 16) {
837                 if (n & 0x01) {
838                         *(uint8_t *)dst = *(const uint8_t *)src;
839                         src = (const uint8_t *)src + 1;
840                         dst = (uint8_t *)dst + 1;
841                 }
842                 if (n & 0x02) {
843                         *(uint16_t *)dst = *(const uint16_t *)src;
844                         src = (const uint16_t *)src + 1;
845                         dst = (uint16_t *)dst + 1;
846                 }
847                 if (n & 0x04) {
848                         *(uint32_t *)dst = *(const uint32_t *)src;
849                         src = (const uint32_t *)src + 1;
850                         dst = (uint32_t *)dst + 1;
851                 }
852                 if (n & 0x08)
853                         *(uint64_t *)dst = *(const uint64_t *)src;
854
855                 return ret;
856         }
857
858         /* Copy 16 <= size <= 32 bytes */
859         if (n <= 32) {
860                 rte_mov16((uint8_t *)dst, (const uint8_t *)src);
861                 rte_mov16((uint8_t *)dst - 16 + n,
862                                 (const uint8_t *)src - 16 + n);
863
864                 return ret;
865         }
866
867         /* Copy 32 < size <= 64 bytes */
868         if (n <= 64) {
869                 rte_mov32((uint8_t *)dst, (const uint8_t *)src);
870                 rte_mov32((uint8_t *)dst - 32 + n,
871                                 (const uint8_t *)src - 32 + n);
872
873                 return ret;
874         }
875
876         /* Copy 64 bytes blocks */
877         for (; n >= 64; n -= 64) {
878                 rte_mov64((uint8_t *)dst, (const uint8_t *)src);
879                 dst = (uint8_t *)dst + 64;
880                 src = (const uint8_t *)src + 64;
881         }
882
883         /* Copy whatever left */
884         rte_mov64((uint8_t *)dst - 64 + n,
885                         (const uint8_t *)src - 64 + n);
886
887         return ret;
888 }
889
890 static inline void *
891 rte_memcpy(void *dst, const void *src, size_t n)
892 {
893         if (!(((uintptr_t)dst | (uintptr_t)src) & ALIGNMENT_MASK))
894                 return rte_memcpy_aligned(dst, src, n);
895         else
896                 return rte_memcpy_generic(dst, src, n);
897 }
898
899 #ifdef __cplusplus
900 }
901 #endif
902
903 #endif /* _RTE_MEMCPY_X86_64_H_ */