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