1 /* 2 * Copyright (c) 2017 Imagination Technologies. 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer 14 * in the documentation and/or other materials provided with 15 * the distribution. 16 * * Neither the name of Imagination Technologies nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <string.h> 34 35 #if !defined(UNALIGNED_INSTR_SUPPORT) 36 /* does target have unaligned lw/ld/ualw/uald instructions? */ 37 #define UNALIGNED_INSTR_SUPPORT 0 38 #if __mips_isa_rev < 6 && !__mips1 39 #undef UNALIGNED_INSTR_SUPPORT 40 #define UNALIGNED_INSTR_SUPPORT 1 41 #endif 42 #endif 43 44 #if !defined(HW_UNALIGNED_SUPPORT) 45 /* Does target have hardware support for unaligned accesses? */ 46 #define HW_UNALIGNED_SUPPORT 0 47 #if __mips_isa_rev >= 6 48 #undef HW_UNALIGNED_SUPPORT 49 #define HW_UNALIGNED_SUPPORT 1 50 #endif 51 #endif 52 53 #define ENABLE_PREFETCH 1 54 55 #if ENABLE_PREFETCH 56 #define PREFETCH(addr) __builtin_prefetch (addr, 0, 1); 57 #else 58 #define PREFETCH(addr) 59 #endif 60 61 #if _MIPS_SIM == _ABIO32 62 typedef unsigned long reg_t; 63 typedef struct 64 { 65 reg_t B0:8, B1:8, B2:8, B3:8; 66 } bits_t; 67 #else 68 typedef unsigned long long reg_t; 69 typedef struct 70 { 71 reg_t B0:8, B1:8, B2:8, B3:8, B4:8, B5:8, B6:8, B7:8; 72 } bits_t; 73 #endif 74 75 typedef union 76 { 77 reg_t v; 78 bits_t b; 79 } bitfields_t; 80 81 #define DO_BYTE(a, i) \ 82 a[i] = bw.b.B##i; \ 83 len--; \ 84 if(!len) return ret; \ 85 86 /* This code is called when aligning a pointer, there are remaining bytes 87 after doing word compares, or architecture does not have some form 88 of unaligned support. */ 89 static inline void * __attribute__ ((always_inline)) 90 do_bytes (void *a, const void *b, unsigned long len, void *ret) 91 { 92 unsigned char *x = (unsigned char *) a; 93 unsigned char *y = (unsigned char *) b; 94 unsigned long i; 95 96 /* 'len' might be zero here, so preloading the first two values 97 before the loop may access unallocated memory. */ 98 for (i = 0; i < len; i++) 99 { 100 *x = *y; 101 x++; 102 y++; 103 } 104 return ret; 105 } 106 107 static inline void * __attribute__ ((always_inline)) 108 do_bytes_backward (void *a, const void *b, unsigned long len, void *ret) 109 { 110 unsigned char *x = (unsigned char *) a; 111 unsigned char *y = (unsigned char *) b; 112 unsigned long i; 113 114 /* 'len' might be zero here, so preloading the first two values 115 before the loop may access unallocated memory. */ 116 for (i = 0; i < len; i++) { 117 *--x = *--y; 118 } 119 return ret; 120 } 121 122 static inline void * __attribute__ ((always_inline)) 123 do_bytes_aligned (void *a, const void *b, unsigned long len, void *ret) 124 { 125 unsigned char *x = (unsigned char *) a; 126 127 if(len > 0) { 128 bitfields_t bw; 129 bw.v = *((reg_t*) b); 130 131 #if __mips64 132 DO_BYTE(x, 0); 133 DO_BYTE(x, 1); 134 DO_BYTE(x, 2); 135 DO_BYTE(x, 3); 136 DO_BYTE(x, 4); 137 DO_BYTE(x, 5); 138 DO_BYTE(x, 6); 139 DO_BYTE(x, 7); 140 #else 141 DO_BYTE(x, 0); 142 DO_BYTE(x, 1); 143 DO_BYTE(x, 2); 144 DO_BYTE(x, 3); 145 #endif 146 } 147 148 return ret; 149 } 150 151 #if !HW_UNALIGNED_SUPPORT 152 #if UNALIGNED_INSTR_SUPPORT 153 /* for MIPS GCC, there are no unaligned builtins - so this struct forces 154 the compiler to treat the pointer access as unaligned. */ 155 struct ulw 156 { 157 reg_t uli; 158 } __attribute__ ((packed)); 159 160 #define STORE_UNALIGNED_8(a, b) \ 161 { \ 162 reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3]; \ 163 reg_t y4 = b[4], y5 = b[5], y6 = b[6], y7 = b[7]; \ 164 a[0].uli = y0; \ 165 a[1].uli = y1; \ 166 a[2].uli = y2; \ 167 a[3].uli = y3; \ 168 a[4].uli = y4; \ 169 a[5].uli = y5; \ 170 a[6].uli = y6; \ 171 a[7].uli = y7; \ 172 } 173 174 #define STORE_UNALIGNED_4(a, b) \ 175 { \ 176 reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3]; \ 177 a[0].uli = y0; \ 178 a[1].uli = y1; \ 179 a[2].uli = y2; \ 180 a[3].uli = y3; \ 181 } 182 183 /* first pointer is not aligned while second pointer is. */ 184 static void * 185 unaligned_words_forward (struct ulw *a, const reg_t * b, 186 unsigned long words, unsigned long bytes, void *ret) 187 { 188 #if ((_MIPS_SIM == _ABIO32) || _MIPS_TUNE_I6400) 189 unsigned long i, words_by_8, words_by_1; 190 words_by_1 = words % 8; 191 words_by_8 = words >> 3; 192 for (; words_by_8 > 0; words_by_8--) { 193 if(words_by_8 != 1) 194 PREFETCH (b + 8); 195 STORE_UNALIGNED_8(a, b); 196 a += 8; 197 b += 8; 198 } 199 #else 200 unsigned long i, words_by_4, words_by_1; 201 words_by_1 = words % 4; 202 words_by_4 = words >> 2; 203 for (; words_by_4 > 0; words_by_4--) { 204 if(words_by_4 != 1) 205 PREFETCH (b + 4); 206 STORE_UNALIGNED_4(a, b); 207 a += 4; 208 b += 4; 209 } 210 #endif 211 212 /* do remaining words. */ 213 for (i = 0; i < words_by_1; i++) { 214 a->uli = *b; 215 a += 1; 216 b += 1; 217 } 218 219 /* mop up any remaining bytes. */ 220 return do_bytes_aligned (a, b, bytes, ret); 221 } 222 223 static void * 224 unaligned_words_backward (struct ulw *a, const reg_t * b, 225 unsigned long words, unsigned long bytes, void *ret) 226 { 227 #if ((_MIPS_SIM == _ABIO32) || _MIPS_TUNE_I6400) 228 unsigned long i, words_by_8, words_by_1; 229 words_by_1 = words % 8; 230 words_by_8 = words >> 3; 231 for (; words_by_8 > 0; words_by_8--) { 232 if(words_by_8 != 1) 233 PREFETCH (b - 16); 234 a -= 8; 235 b -= 8; 236 STORE_UNALIGNED_8(a, b); 237 } 238 #else 239 unsigned long i, words_by_4, words_by_1; 240 words_by_1 = words % 4; 241 words_by_4 = words >> 2; 242 for (; words_by_4 > 0; words_by_4--) { 243 if(words_by_4 != 1) 244 PREFETCH (b - 8); 245 a -= 4; 246 b -= 4; 247 STORE_UNALIGNED_4(a, b); 248 } 249 #endif 250 251 /* do remaining words. */ 252 for (i = 0; i < words_by_1; i++) { 253 a -= 1; 254 b -= 1; 255 a->uli = *b; 256 } 257 258 /* mop up any remaining bytes. */ 259 return do_bytes_backward (a, b, bytes, ret); 260 } 261 262 #else 263 /* no HW support or unaligned lw/ld/ualw/uald instructions. */ 264 static void * 265 unaligned_words_forward (reg_t * a, const reg_t * b, 266 unsigned long words, unsigned long bytes, void *ret) 267 { 268 return do_bytes_aligned (a, b, (sizeof (reg_t) * words) + bytes, ret); 269 } 270 271 static void * 272 unaligned_words_backward (reg_t * a, const reg_t * b, 273 unsigned long words, unsigned long bytes, void *ret) 274 { 275 return do_bytes_backward (a, b, (sizeof (reg_t) * words) + bytes, ret); 276 } 277 278 #endif /* UNALIGNED_INSTR_SUPPORT */ 279 #endif /* HW_UNALIGNED_SUPPORT */ 280 281 /* both pointers are aligned, or first isn't and HW support for unaligned. */ 282 283 #define STORE_ALIGNED_8(a, b) \ 284 { \ 285 reg_t x0 = b[0], x1 = b[1], x2 = b[2], x3 = b[3]; \ 286 reg_t x4 = b[4], x5 = b[5], x6 = b[6], x7 = b[7]; \ 287 a[0] = x0; \ 288 a[1] = x1; \ 289 a[2] = x2; \ 290 a[3] = x3; \ 291 a[4] = x4; \ 292 a[5] = x5; \ 293 a[6] = x6; \ 294 a[7] = x7; \ 295 } 296 297 #define STORE_ALIGNED_4(a, b) \ 298 { \ 299 reg_t x0 = b[0], x1 = b[1], x2 = b[2], x3 = b[3]; \ 300 a[0] = x0; \ 301 a[1] = x1; \ 302 a[2] = x2; \ 303 a[3] = x3; \ 304 } 305 306 static void * 307 aligned_words_forward (reg_t * a, const reg_t * b, 308 unsigned long words, unsigned long bytes, void *ret) 309 { 310 #if ((_MIPS_SIM == _ABIO32) || _MIPS_TUNE_I6400) 311 unsigned long i, words_by_8, words_by_1; 312 words_by_1 = words % 8; 313 words_by_8 = words >> 3; 314 for (; words_by_8 > 0; words_by_8--) { 315 if(words_by_8 != 1) 316 PREFETCH (b + 8); 317 STORE_ALIGNED_8(a, b); 318 a += 8; 319 b += 8; 320 } 321 #else 322 unsigned long i, words_by_4, words_by_1; 323 words_by_1 = words % 4; 324 words_by_4 = words >> 2; 325 for (; words_by_4 > 0; words_by_4--) { 326 if(words_by_4 != 1) 327 PREFETCH (b + 4); 328 STORE_ALIGNED_4(a, b); 329 a += 4; 330 b += 4; 331 } 332 #endif 333 334 /* do remaining words. */ 335 for (i = 0; i < words_by_1; i++) { 336 *a = *b; 337 a += 1; 338 b += 1; 339 } 340 341 /* mop up any remaining bytes. */ 342 return do_bytes_aligned (a, b, bytes, ret); 343 } 344 345 346 static void * 347 aligned_words_backward (reg_t * a, const reg_t * b, 348 unsigned long words, unsigned long bytes, void *ret) 349 { 350 #if ((_MIPS_SIM == _ABIO32) || _MIPS_TUNE_I6400) 351 unsigned long i, words_by_8, words_by_1; 352 words_by_1 = words % 8; 353 words_by_8 = words >> 3; 354 for (; words_by_8 > 0; words_by_8--) { 355 if(words_by_8 != 1) 356 PREFETCH (b - 16); 357 a -= 8; 358 b -= 8; 359 STORE_ALIGNED_8(a, b); 360 } 361 #else 362 unsigned long i, words_by_4, words_by_1; 363 words_by_1 = words % 4; 364 words_by_4 = words >> 2; 365 for (; words_by_4 > 0; words_by_4--) { 366 if(words_by_4 != 1) 367 PREFETCH (b - 8); 368 a -= 4; 369 b -= 4; 370 STORE_ALIGNED_4(a, b); 371 } 372 #endif 373 374 /* do remaining words. */ 375 for (i = 0; i < words_by_1; i++) { 376 a -= 1; 377 b -= 1; 378 *a = *b; 379 } 380 381 /* mop up any remaining bytes. */ 382 return do_bytes_backward (a, b, bytes, ret); 383 } 384 385 void * 386 memmove (void *dst0, const void *src0, size_t length) 387 { 388 unsigned long bytes, words; 389 void *ret = dst0; 390 391 if (length == 0 || dst0 == src0) /* nothing to do */ 392 return dst0; 393 394 if ((unsigned long)dst0 < (unsigned long)src0) { 395 /* Copy forwards. */ 396 /* This shouldn't hit that often. */ 397 if (length < sizeof (reg_t) * 4) { 398 return do_bytes (dst0, src0, length, ret); 399 } 400 401 /* Align the second pointer to word/dword alignment. 402 Note that the pointer is only 32-bits for o32/n32 ABIs. For 403 n32, loads are done as 64-bit while address remains 32-bit. */ 404 bytes = ((unsigned long) src0) % sizeof (reg_t); 405 if (bytes) { 406 bytes = sizeof (reg_t) - bytes; 407 if (bytes > length) 408 bytes = length; 409 do_bytes (dst0, src0, bytes, ret); 410 if (length == bytes) 411 return ret; 412 length -= bytes; 413 dst0 = (void *) (((unsigned char *) dst0) + bytes); 414 src0 = (const void *) (((unsigned char *) src0) + bytes); 415 } 416 417 /* Second pointer now aligned. */ 418 words = length / sizeof (reg_t); 419 bytes = length % sizeof (reg_t); 420 #if HW_UNALIGNED_SUPPORT 421 /* treat possible unaligned first pointer as aligned. */ 422 return aligned_words_forward (dst0, src0, words, bytes, ret); 423 #else 424 if (((unsigned long) dst0) % sizeof (reg_t) == 0) { 425 return aligned_words_forward (dst0, src0, words, bytes, ret); 426 } 427 /* need to use unaligned instructions on first pointer. */ 428 return unaligned_words_forward (dst0, src0, words, bytes, ret); 429 #endif 430 } else { 431 /* Copy backwards. */ 432 dst0 = (void *) (((unsigned char *) dst0) + length); 433 src0 = (const void *) (((unsigned char *) src0) + length); 434 435 /* This shouldn't hit that often. */ 436 if (length < sizeof (reg_t) * 4) { 437 return do_bytes_backward (dst0, src0, length, ret); 438 } 439 440 /* Align the second pointer to word/dword alignment. 441 Note that the pointer is only 32-bits for o32/n32 ABIs. For 442 n32, loads are done as 64-bit while address remains 32-bit. */ 443 bytes = ((unsigned long) src0) % sizeof (reg_t); 444 if (bytes) { 445 if (bytes > length) 446 bytes = length; 447 do_bytes_backward (dst0, src0, bytes, ret); 448 if (length == bytes) 449 return ret; 450 length -= bytes; 451 dst0 = (void *) (((unsigned char *) dst0) - bytes); 452 src0 = (const void *) (((unsigned char *) src0) - bytes); 453 } 454 455 words = length / sizeof (reg_t); 456 bytes = length % sizeof (reg_t); 457 #if HW_UNALIGNED_SUPPORT 458 /* treat possible unaligned first pointer as aligned. */ 459 return aligned_words_backward ((void *)dst0, (void *)src0, words, bytes, ret); 460 #else 461 if (((unsigned long) dst0) % sizeof (reg_t) == 0) { 462 return aligned_words_backward (dst0, src0, words, bytes, ret); 463 } 464 /* need to use unaligned instructions on first pointer. */ 465 return unaligned_words_backward (dst0, src0, words, bytes, ret); 466 #endif 467 } 468 } 469