remove version in all files
[dpdk.git] / lib / librte_eal / common / include / rte_memcpy.h
1 /*-
2  *   BSD LICENSE
3  * 
4  *   Copyright(c) 2010-2012 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
35 #ifndef _RTE_MEMCPY_H_
36 #define _RTE_MEMCPY_H_
37
38 /**
39  * @file
40  *
41  * Functions for SSE implementation of memcpy().
42  */
43
44 #include <stdint.h>
45 #include <string.h>
46
47 #ifdef __cplusplus
48 extern "C" {
49 #endif
50
51 /**
52  * Copy 16 bytes from one location to another using optimised SSE
53  * instructions. The locations should not overlap.
54  *
55  * @param dst
56  *   Pointer to the destination of the data.
57  * @param src
58  *   Pointer to the source data.
59  */
60 static inline void
61 rte_mov16(uint8_t *dst, const uint8_t *src)
62 {
63         asm volatile ("movdqu (%[src]), %%xmm0\n\t"
64                       "movdqu %%xmm0, (%[dst])\n\t"
65                       :
66                       : [src] "r" (src),
67                         [dst] "r"(dst)
68                       : "xmm0", "memory");
69 }
70
71 /**
72  * Copy 32 bytes from one location to another using optimised SSE
73  * instructions. The locations should not overlap.
74  *
75  * @param dst
76  *   Pointer to the destination of the data.
77  * @param src
78  *   Pointer to the source data.
79  */
80 static inline void
81 rte_mov32(uint8_t *dst, const uint8_t *src)
82 {
83         asm volatile ("movdqu (%[src]), %%xmm0\n\t"
84                       "movdqu 16(%[src]), %%xmm1\n\t"
85                       "movdqu %%xmm0, (%[dst])\n\t"
86                       "movdqu %%xmm1, 16(%[dst])"
87                       :
88                       : [src] "r" (src),
89                         [dst] "r"(dst)
90                       : "xmm0", "xmm1", "memory");
91 }
92
93 /**
94  * Copy 48 bytes from one location to another using optimised SSE
95  * instructions. The locations should not overlap.
96  *
97  * @param dst
98  *   Pointer to the destination of the data.
99  * @param src
100  *   Pointer to the source data.
101  */
102 static inline void
103 rte_mov48(uint8_t *dst, const uint8_t *src)
104 {
105         asm volatile ("movdqu (%[src]), %%xmm0\n\t"
106                       "movdqu 16(%[src]), %%xmm1\n\t"
107                       "movdqu 32(%[src]), %%xmm2\n\t"
108                       "movdqu %%xmm0, (%[dst])\n\t"
109                       "movdqu %%xmm1, 16(%[dst])\n\t"
110                       "movdqu %%xmm2, 32(%[dst])"
111                       :
112                       : [src] "r" (src),
113                         [dst] "r"(dst)
114                       : "xmm0", "xmm1", "memory");
115 }
116
117 /**
118  * Copy 64 bytes from one location to another using optimised SSE
119  * instructions. The locations should not overlap.
120  *
121  * @param dst
122  *   Pointer to the destination of the data.
123  * @param src
124  *   Pointer to the source data.
125  */
126 static inline void
127 rte_mov64(uint8_t *dst, const uint8_t *src)
128 {
129         asm volatile ("movdqu (%[src]), %%xmm0\n\t"
130                       "movdqu 16(%[src]), %%xmm1\n\t"
131                       "movdqu 32(%[src]), %%xmm2\n\t"
132                       "movdqu 48(%[src]), %%xmm3\n\t"
133                       "movdqu %%xmm0, (%[dst])\n\t"
134                       "movdqu %%xmm1, 16(%[dst])\n\t"
135                       "movdqu %%xmm2, 32(%[dst])\n\t"
136                       "movdqu %%xmm3, 48(%[dst])"
137                       :
138                       : [src] "r" (src),
139                         [dst] "r"(dst)
140                       : "xmm0", "xmm1", "xmm2", "xmm3","memory");
141 }
142
143 /**
144  * Copy 128 bytes from one location to another using optimised SSE
145  * instructions. The locations should not overlap.
146  *
147  * @param dst
148  *   Pointer to the destination of the data.
149  * @param src
150  *   Pointer to the source data.
151  */
152 static inline void
153 rte_mov128(uint8_t *dst, const uint8_t *src)
154 {
155         asm volatile ("movdqu (%[src]), %%xmm0\n\t"
156                       "movdqu 16(%[src]), %%xmm1\n\t"
157                       "movdqu 32(%[src]), %%xmm2\n\t"
158                       "movdqu 48(%[src]), %%xmm3\n\t"
159                       "movdqu 64(%[src]), %%xmm4\n\t"
160                       "movdqu 80(%[src]), %%xmm5\n\t"
161                       "movdqu 96(%[src]), %%xmm6\n\t"
162                       "movdqu 112(%[src]), %%xmm7\n\t"
163                       "movdqu %%xmm0, (%[dst])\n\t"
164                       "movdqu %%xmm1, 16(%[dst])\n\t"
165                       "movdqu %%xmm2, 32(%[dst])\n\t"
166                       "movdqu %%xmm3, 48(%[dst])\n\t"
167                       "movdqu %%xmm4, 64(%[dst])\n\t"
168                       "movdqu %%xmm5, 80(%[dst])\n\t"
169                       "movdqu %%xmm6, 96(%[dst])\n\t"
170                       "movdqu %%xmm7, 112(%[dst])"
171                       :
172                       : [src] "r" (src),
173                         [dst] "r"(dst)
174                       : "xmm0", "xmm1", "xmm2", "xmm3",
175                         "xmm4", "xmm5", "xmm6", "xmm7", "memory");
176 }
177
178 /**
179  * Copy 256 bytes from one location to another using optimised SSE
180  * instructions. The locations should not overlap.
181  *
182  * @param dst
183  *   Pointer to the destination of the data.
184  * @param src
185  *   Pointer to the source data.
186  */
187 static inline void
188 rte_mov256(uint8_t *dst, const uint8_t *src)
189 {
190         /*
191          * There are 16XMM registers, but this function does not use
192          * them all so that it can still be compiled as 32bit
193          * code. The performance increase was neglible if all 16
194          * registers were used.
195          */
196         rte_mov128(dst, src);
197         rte_mov128(dst + 128, src + 128);
198 }
199
200 #ifdef RTE_MEMCPY_BUILTIN_CONSTANT_P
201 /**
202  * Choose between compiler built-in implementation of memcpy or DPDK
203  * implementation depending if size is a compile-time constant
204  */
205 #define rte_memcpy(dst, src, n) \
206         (__builtin_constant_p (n) ? \
207         memcpy(dst, src, n) : rte_memcpy_func(dst, src, n))
208 #else
209 /**
210  * Always use DPDK implementation.
211  */
212 #define rte_memcpy rte_memcpy_func
213 #endif
214
215 /**
216  * Copy bytes from one location to another. The locations must not overlap.
217  *
218  * @param dst
219  *   Pointer to the destination of the data.
220  * @param src
221  *   Pointer to the source data.
222  * @param n
223  *   Number of bytes to copy.
224  * @return
225  *   Pointer to the destination data.
226  */
227 static inline void *
228 rte_memcpy_func(void *dst, const void *src, size_t n)
229 {
230         void *ret = dst;
231
232         /* We can't copy < 16 bytes using XMM registers so do it manually. */
233         if (n < 16) {
234                 if (n & 0x01) {
235                         *(uint8_t *)dst = *(const uint8_t *)src;
236                         dst = (uint8_t *)dst + 1;
237                         src = (const uint8_t *)src + 1;
238                 }
239                 if (n & 0x02) {
240                         *(uint16_t *)dst = *(const uint16_t *)src;
241                         dst = (uint16_t *)dst + 1;
242                         src = (const uint16_t *)src + 1;
243                 }
244                 if (n & 0x04) {
245                         /*
246                          * NOTE: doing this as a 32bit copy causes "strict
247                          * aliasing" compile errors, but worked fine for 64bit
248                          * copy below, for unknown reasons.
249                          */
250                         *(uint16_t *)dst = *(const uint16_t *)src;
251                         *((uint16_t *)dst + 1) = *((const uint16_t *)src + 1);
252                         dst = (uint32_t *)dst + 1;
253                         src = (const uint32_t *)src + 1;
254                 }
255                 if (n & 0x08) {
256                         *(uint64_t *)dst = *(const uint64_t *)src;
257                 }
258                 return ret;
259         }
260
261         /* Special fast cases for <= 128 bytes */
262         if (n <= 32) {
263                 rte_mov16((uint8_t *)dst, (const uint8_t *)src);
264                 rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
265                 return ret;
266         }
267
268         if (n <= 64) {
269                 rte_mov32((uint8_t *)dst, (const uint8_t *)src);
270                 rte_mov32((uint8_t *)dst - 32 + n, (const uint8_t *)src - 32 + n);
271                 return ret;
272         }
273
274         if (n <= 128) {
275                 rte_mov64((uint8_t *)dst, (const uint8_t *)src);
276                 rte_mov64((uint8_t *)dst - 64 + n, (const uint8_t *)src - 64 + n);
277                 return ret;
278         }
279
280         /*
281          * For large copies > 128 bytes. This combination of 256, 64 and 16 byte
282          * copies was found to be faster than doing 128 and 32 byte copies as
283          * well.
284          */
285         for ( ; n >= 256; n -= 256) {
286                 rte_mov256((uint8_t *)dst, (const uint8_t *)src);
287                 dst = (uint8_t *)dst + 256;
288                 src = (const uint8_t *)src + 256;
289         }
290
291         /*
292          * We split the remaining bytes (which will be less than 256) into
293          * 64byte (2^6) chunks.
294          * Using incrementing integers in the case labels of a switch statement
295          * enourages the compiler to use a jump table. To get incrementing
296          * integers, we shift the 2 relevant bits to the LSB position to first
297          * get decrementing integers, and then subtract.
298          */
299         switch (3 - (n >> 6)) {
300         case 0x00:
301                 rte_mov64((uint8_t *)dst, (const uint8_t *)src);
302                 n -= 64;
303                 dst = (uint8_t *)dst + 64;
304                 src = (const uint8_t *)src + 64;      /* fallthrough */
305         case 0x01:
306                 rte_mov64((uint8_t *)dst, (const uint8_t *)src);
307                 n -= 64;
308                 dst = (uint8_t *)dst + 64;
309                 src = (const uint8_t *)src + 64;      /* fallthrough */
310         case 0x02:
311                 rte_mov64((uint8_t *)dst, (const uint8_t *)src);
312                 n -= 64;
313                 dst = (uint8_t *)dst + 64;
314                 src = (const uint8_t *)src + 64;      /* fallthrough */
315         default:
316                 ;
317         }
318
319         /*
320          * We split the remaining bytes (which will be less than 64) into
321          * 16byte (2^4) chunks, using the same switch structure as above.
322          */
323         switch (3 - (n >> 4)) {
324         case 0x00:
325                 rte_mov16((uint8_t *)dst, (const uint8_t *)src);
326                 n -= 16;
327                 dst = (uint8_t *)dst + 16;
328                 src = (const uint8_t *)src + 16;      /* fallthrough */
329         case 0x01:
330                 rte_mov16((uint8_t *)dst, (const uint8_t *)src);
331                 n -= 16;
332                 dst = (uint8_t *)dst + 16;
333                 src = (const uint8_t *)src + 16;      /* fallthrough */
334         case 0x02:
335                 rte_mov16((uint8_t *)dst, (const uint8_t *)src);
336                 n -= 16;
337                 dst = (uint8_t *)dst + 16;
338                 src = (const uint8_t *)src + 16;      /* fallthrough */
339         default:
340                 ;
341         }
342
343         /* Copy any remaining bytes, without going beyond end of buffers */
344         if (n != 0) {
345                 rte_mov16((uint8_t *)dst - 16 + n, (const uint8_t *)src - 16 + n);
346         }
347         return ret;
348 }
349
350 #ifdef __cplusplus
351 }
352 #endif
353
354 #endif /* _RTE_MEMCPY_H_ */