1 // SPDX-License-Identifier: GPL-2.0 << 2 /* << 3 * linux/arch/alpha/lib/memcpy.c << 4 * << 5 * Copyright (C) 1995 Linus Torvalds << 6 */ << 7 << 8 /* << 9 * This is a reasonably optimized memcpy() rou << 10 */ << 11 << 12 /* << 13 * Note that the C code is written to be optim << 14 * at this point gcc is unable to sanely compi << 15 * explicit compare against 0 (instead of just << 16 * "bge reg, xx"). I hope alpha-gcc will be fi << 17 */ << 18 << 19 #include <linux/types.h> 1 #include <linux/types.h> 20 #include <linux/export.h> << 21 #include <linux/string.h> << 22 << 23 /* << 24 * This should be done in one go with ldq_u*2/ << 25 * with a macro so that we can fix it up later << 26 */ << 27 #define ALIGN_DEST_TO8_UP(d,s,n) \ << 28 while (d & 7) { \ << 29 if (n <= 0) return; \ << 30 n--; \ << 31 *(char *) d = *(char *) s; \ << 32 d++; s++; \ << 33 } << 34 #define ALIGN_DEST_TO8_DN(d,s,n) \ << 35 while (d & 7) { \ << 36 if (n <= 0) return; \ << 37 n--; \ << 38 d--; s--; \ << 39 *(char *) d = *(char *) s; \ << 40 } << 41 << 42 /* << 43 * This should similarly be done with ldq_u*2/ << 44 * is aligned, but we don't fill in a full qua << 45 */ << 46 #define DO_REST_UP(d,s,n) \ << 47 while (n > 0) { \ << 48 n--; \ << 49 *(char *) d = *(char *) s; \ << 50 d++; s++; \ << 51 } << 52 #define DO_REST_DN(d,s,n) \ << 53 while (n > 0) { \ << 54 n--; \ << 55 d--; s--; \ << 56 *(char *) d = *(char *) s; \ << 57 } << 58 2 59 /* !! 3 void * memcpy(void * to, const void * from, size_t n) 60 * This should be done with ldq/mask/stq. The << 61 * aligned, but we don't fill in a full quad-w << 62 */ << 63 #define DO_REST_ALIGNED_UP(d,s,n) DO_REST_UP(d << 64 #define DO_REST_ALIGNED_DN(d,s,n) DO_REST_DN(d << 65 << 66 /* << 67 * This does unaligned memory copies. We want << 68 * an unaligned address, as that would do a re << 69 * We also want to avoid double-reading the un << 70 * << 71 * Note the ordering to try to avoid load (and << 72 */ << 73 static inline void __memcpy_unaligned_up (unsi << 74 long << 75 { 4 { 76 ALIGN_DEST_TO8_UP(d,s,n); !! 5 void *xto = to; 77 n -= 8; /* to avoid co !! 6 size_t temp, temp1; 78 if (n >= 0) { << 79 unsigned long low_word, high_w << 80 __asm__("ldq_u %0,%1":"=r" (lo << 81 do { << 82 unsigned long tmp; << 83 __asm__("ldq_u %0,%1": << 84 n -= 8; << 85 __asm__("extql %1,%2,% << 86 :"=r" (low_wor << 87 :"r" (low_word << 88 __asm__("extqh %1,%2,% << 89 :"=r" (tmp) << 90 :"r" (high_wor << 91 s += 8; << 92 *(unsigned long *) d = << 93 d += 8; << 94 low_word = high_word; << 95 } while (n >= 0); << 96 } << 97 n += 8; << 98 DO_REST_UP(d,s,n); << 99 } << 100 7 101 static inline void __memcpy_unaligned_dn (unsi !! 8 if (!n) 102 long !! 9 return xto; 103 { !! 10 if ((long) to & 1) 104 /* I don't understand AXP assembler we !! 11 { 105 s += n; !! 12 char *cto = to; 106 d += n; !! 13 const char *cfrom = from; 107 while (n--) !! 14 *cto++ = *cfrom++; 108 * (char *) --d = * (char *) -- !! 15 to = cto; 109 } !! 16 from = cfrom; 110 !! 17 n--; 111 /* !! 18 } 112 * Hmm.. Strange. The __asm__ here is there to !! 19 if (n > 2 && (long) to & 2) 113 * for the load-store. I don't know why, but i !! 20 { 114 * point register for the move seems to slow t !! 21 short *sto = to; 115 * though). !! 22 const short *sfrom = from; 116 * !! 23 *sto++ = *sfrom++; 117 * Note the ordering to try to avoid load (and !! 24 to = sto; 118 */ !! 25 from = sfrom; 119 static inline void __memcpy_aligned_up (unsign !! 26 n -= 2; 120 long n !! 27 } 121 { !! 28 temp = n >> 2; 122 ALIGN_DEST_TO8_UP(d,s,n); !! 29 if (temp) 123 n -= 8; !! 30 { 124 while (n >= 0) { !! 31 long *lto = to; 125 unsigned long tmp; !! 32 const long *lfrom = from; 126 __asm__("ldq %0,%1":"=r" (tmp) !! 33 127 n -= 8; !! 34 __asm__ __volatile__("movel %2,%3\n\t" 128 s += 8; !! 35 "andw #7,%3\n\t" 129 *(unsigned long *) d = tmp; !! 36 "lsrl #3,%2\n\t" 130 d += 8; !! 37 "negw %3\n\t" 131 } !! 38 "jmp %%pc@(1f,%3:w:2)\n\t" 132 n += 8; !! 39 "4:\t" 133 DO_REST_ALIGNED_UP(d,s,n); !! 40 "movel %0@+,%1@+\n\t" 134 } !! 41 "movel %0@+,%1@+\n\t" 135 static inline void __memcpy_aligned_dn (unsign !! 42 "movel %0@+,%1@+\n\t" 136 long n !! 43 "movel %0@+,%1@+\n\t" 137 { !! 44 "movel %0@+,%1@+\n\t" 138 s += n; !! 45 "movel %0@+,%1@+\n\t" 139 d += n; !! 46 "movel %0@+,%1@+\n\t" 140 ALIGN_DEST_TO8_DN(d,s,n); !! 47 "movel %0@+,%1@+\n\t" 141 n -= 8; !! 48 "1:\t" 142 while (n >= 0) { !! 49 "dbra %2,4b\n\t" 143 unsigned long tmp; !! 50 "clrw %2\n\t" 144 s -= 8; !! 51 "subql #1,%2\n\t" 145 __asm__("ldq %0,%1":"=r" (tmp) !! 52 "jpl 4b\n\t" 146 n -= 8; !! 53 : "=a" (lfrom), "=a" (lto), "=d" (temp), 147 d -= 8; !! 54 "=&d" (temp1) 148 *(unsigned long *) d = tmp; !! 55 : "" (lfrom), "1" (lto), "2" (temp) 149 } !! 56 ); 150 n += 8; !! 57 to = lto; 151 DO_REST_ALIGNED_DN(d,s,n); !! 58 from = lfrom; 152 } !! 59 } 153 !! 60 if (n & 2) 154 #undef memcpy !! 61 { 155 !! 62 short *sto = to; 156 void * memcpy(void * dest, const void *src, si !! 63 const short *sfrom = from; 157 { !! 64 *sto++ = *sfrom++; 158 if (!(((unsigned long) dest ^ (unsigne !! 65 to = sto; 159 __memcpy_aligned_up ((unsigned !! 66 from = sfrom; 160 n); !! 67 } 161 return dest; !! 68 if (n & 1) 162 } !! 69 { 163 __memcpy_unaligned_up ((unsigned long) !! 70 char *cto = to; 164 return dest; !! 71 const char *cfrom = from; >> 72 *cto = *cfrom; >> 73 } >> 74 return xto; 165 } 75 } 166 EXPORT_SYMBOL(memcpy); << 167 76
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.