1 // SPDX-License-Identifier: GPL-2.0 << 2 /* 1 /* 3 * linux/arch/alpha/lib/memcpy.c !! 2 * This file is subject to the terms and conditions of the GNU General Public 4 * !! 3 * License. See the file COPYING in the main directory of this archive 5 * Copyright (C) 1995 Linus Torvalds !! 4 * for more details. 6 */ 5 */ 7 6 8 /* !! 7 #include <linux/module.h> 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> << 20 #include <linux/export.h> << 21 #include <linux/string.h> 8 #include <linux/string.h> 22 9 23 /* !! 10 void *memcpy(void *to, const void *from, size_t n) 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 << 59 /* << 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 { << 76 ALIGN_DEST_TO8_UP(d,s,n); << 77 n -= 8; /* to avoid co << 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 << 101 static inline void __memcpy_unaligned_dn (unsi << 102 long << 103 { 11 { 104 /* I don't understand AXP assembler we !! 12 void *xto = to; 105 s += n; !! 13 size_t temp; 106 d += n; << 107 while (n--) << 108 * (char *) --d = * (char *) -- << 109 } << 110 14 111 /* !! 15 if (!n) 112 * Hmm.. Strange. The __asm__ here is there to !! 16 return xto; 113 * for the load-store. I don't know why, but i !! 17 if ((long)to & 1) { 114 * point register for the move seems to slow t !! 18 char *cto = to; 115 * though). !! 19 const char *cfrom = from; 116 * !! 20 *cto++ = *cfrom++; 117 * Note the ordering to try to avoid load (and !! 21 to = cto; 118 */ !! 22 from = cfrom; 119 static inline void __memcpy_aligned_up (unsign !! 23 n--; 120 long n !! 24 } 121 { !! 25 #if defined(CONFIG_M68000) 122 ALIGN_DEST_TO8_UP(d,s,n); !! 26 if ((long)from & 1) { 123 n -= 8; !! 27 char *cto = to; 124 while (n >= 0) { !! 28 const char *cfrom = from; 125 unsigned long tmp; !! 29 for (; n; n--) 126 __asm__("ldq %0,%1":"=r" (tmp) !! 30 *cto++ = *cfrom++; 127 n -= 8; !! 31 return xto; 128 s += 8; !! 32 } 129 *(unsigned long *) d = tmp; !! 33 #endif 130 d += 8; !! 34 if (n > 2 && (long)to & 2) { 131 } !! 35 short *sto = to; 132 n += 8; !! 36 const short *sfrom = from; 133 DO_REST_ALIGNED_UP(d,s,n); !! 37 *sto++ = *sfrom++; 134 } !! 38 to = sto; 135 static inline void __memcpy_aligned_dn (unsign !! 39 from = sfrom; 136 long n !! 40 n -= 2; 137 { !! 41 } 138 s += n; !! 42 temp = n >> 2; 139 d += n; !! 43 if (temp) { 140 ALIGN_DEST_TO8_DN(d,s,n); !! 44 long *lto = to; 141 n -= 8; !! 45 const long *lfrom = from; 142 while (n >= 0) { !! 46 #if defined(CONFIG_M68000) || defined(CONFIG_COLDFIRE) 143 unsigned long tmp; !! 47 for (; temp; temp--) 144 s -= 8; !! 48 *lto++ = *lfrom++; 145 __asm__("ldq %0,%1":"=r" (tmp) !! 49 #else 146 n -= 8; !! 50 size_t temp1; 147 d -= 8; !! 51 asm volatile ( 148 *(unsigned long *) d = tmp; !! 52 " movel %2,%3\n" 149 } !! 53 " andw #7,%3\n" 150 n += 8; !! 54 " lsrl #3,%2\n" 151 DO_REST_ALIGNED_DN(d,s,n); !! 55 " negw %3\n" 152 } !! 56 " jmp %%pc@(1f,%3:w:2)\n" 153 !! 57 "4: movel %0@+,%1@+\n" 154 #undef memcpy !! 58 " movel %0@+,%1@+\n" 155 !! 59 " movel %0@+,%1@+\n" 156 void * memcpy(void * dest, const void *src, si !! 60 " movel %0@+,%1@+\n" 157 { !! 61 " movel %0@+,%1@+\n" 158 if (!(((unsigned long) dest ^ (unsigne !! 62 " movel %0@+,%1@+\n" 159 __memcpy_aligned_up ((unsigned !! 63 " movel %0@+,%1@+\n" 160 n); !! 64 " movel %0@+,%1@+\n" 161 return dest; !! 65 "1: dbra %2,4b\n" >> 66 " clrw %2\n" >> 67 " subql #1,%2\n" >> 68 " jpl 4b" >> 69 : "=a" (lfrom), "=a" (lto), "=d" (temp), "=&d" (temp1) >> 70 : "" (lfrom), "1" (lto), "2" (temp)); >> 71 #endif >> 72 to = lto; >> 73 from = lfrom; >> 74 } >> 75 if (n & 2) { >> 76 short *sto = to; >> 77 const short *sfrom = from; >> 78 *sto++ = *sfrom++; >> 79 to = sto; >> 80 from = sfrom; >> 81 } >> 82 if (n & 1) { >> 83 char *cto = to; >> 84 const char *cfrom = from; >> 85 *cto = *cfrom; 162 } 86 } 163 __memcpy_unaligned_up ((unsigned long) !! 87 return xto; 164 return dest; << 165 } 88 } 166 EXPORT_SYMBOL(memcpy); 89 EXPORT_SYMBOL(memcpy); 167 90
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.