memcpy.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:4k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  *  linux/arch/alpha/lib/memcpy.c
  3.  *
  4.  *  Copyright (C) 1995  Linus Torvalds
  5.  */
  6. /*
  7.  * This is a reasonably optimized memcpy() routine.
  8.  */
  9. /*
  10.  * Note that the C code is written to be optimized into good assembly. However,
  11.  * at this point gcc is unable to sanely compile "if (n >= 0)", resulting in a
  12.  * explicit compare against 0 (instead of just using the proper "blt reg, xx" or
  13.  * "bge reg, xx"). I hope alpha-gcc will be fixed to notice this eventually..
  14.  */
  15. #include <linux/types.h>
  16. /*
  17.  * This should be done in one go with ldq_u*2/mask/stq_u. Do it
  18.  * with a macro so that we can fix it up later..
  19.  */
  20. #define ALIGN_DEST_TO8_UP(d,s,n) 
  21. while (d & 7) { 
  22. if (n <= 0) return; 
  23. n--; 
  24. *(char *) d = *(char *) s; 
  25. d++; s++; 
  26. }
  27. #define ALIGN_DEST_TO8_DN(d,s,n) 
  28. while (d & 7) { 
  29. if (n <= 0) return; 
  30. n--; 
  31. d--; s--; 
  32. *(char *) d = *(char *) s; 
  33. }
  34. /*
  35.  * This should similarly be done with ldq_u*2/mask/stq. The destination
  36.  * is aligned, but we don't fill in a full quad-word
  37.  */
  38. #define DO_REST_UP(d,s,n) 
  39. while (n > 0) { 
  40. n--; 
  41. *(char *) d = *(char *) s; 
  42. d++; s++; 
  43. }
  44. #define DO_REST_DN(d,s,n) 
  45. while (n > 0) { 
  46. n--; 
  47. d--; s--; 
  48. *(char *) d = *(char *) s; 
  49. }
  50. /*
  51.  * This should be done with ldq/mask/stq. The source and destination are
  52.  * aligned, but we don't fill in a full quad-word
  53.  */
  54. #define DO_REST_ALIGNED_UP(d,s,n) DO_REST_UP(d,s,n)
  55. #define DO_REST_ALIGNED_DN(d,s,n) DO_REST_DN(d,s,n)
  56. /*
  57.  * This does unaligned memory copies. We want to avoid storing to
  58.  * an unaligned address, as that would do a read-modify-write cycle.
  59.  * We also want to avoid double-reading the unaligned reads.
  60.  *
  61.  * Note the ordering to try to avoid load (and address generation) latencies.
  62.  */
  63. static inline void __memcpy_unaligned_up (unsigned long d, unsigned long s,
  64.   long n)
  65. {
  66. ALIGN_DEST_TO8_UP(d,s,n);
  67. n -= 8; /* to avoid compare against 8 in the loop */
  68. if (n >= 0) {
  69. unsigned long low_word, high_word;
  70. __asm__("ldq_u %0,%1":"=r" (low_word):"m" (*(unsigned long *) s));
  71. do {
  72. unsigned long tmp;
  73. __asm__("ldq_u %0,%1":"=r" (high_word):"m" (*(unsigned long *)(s+8)));
  74. n -= 8;
  75. __asm__("extql %1,%2,%0"
  76. :"=r" (low_word)
  77. :"r" (low_word), "r" (s));
  78. __asm__("extqh %1,%2,%0"
  79. :"=r" (tmp)
  80. :"r" (high_word), "r" (s));
  81. s += 8;
  82. *(unsigned long *) d = low_word | tmp;
  83. d += 8;
  84. low_word = high_word;
  85. } while (n >= 0);
  86. }
  87. n += 8;
  88. DO_REST_UP(d,s,n);
  89. }
  90. static inline void __memcpy_unaligned_dn (unsigned long d, unsigned long s,
  91.   long n)
  92. {
  93. /* I don't understand AXP assembler well enough for this. -Tim */
  94. s += n;
  95. d += n;
  96. while (n--)
  97. * (char *) --d = * (char *) --s;
  98. }
  99. /*
  100.  * Hmm.. Strange. The __asm__ here is there to make gcc use an integer register
  101.  * for the load-store. I don't know why, but it would seem that using a floating
  102.  * point register for the move seems to slow things down (very small difference,
  103.  * though).
  104.  *
  105.  * Note the ordering to try to avoid load (and address generation) latencies.
  106.  */
  107. static inline void __memcpy_aligned_up (unsigned long d, unsigned long s,
  108. long n)
  109. {
  110. ALIGN_DEST_TO8_UP(d,s,n);
  111. n -= 8;
  112. while (n >= 0) {
  113. unsigned long tmp;
  114. __asm__("ldq %0,%1":"=r" (tmp):"m" (*(unsigned long *) s));
  115. n -= 8;
  116. s += 8;
  117. *(unsigned long *) d = tmp;
  118. d += 8;
  119. }
  120. n += 8;
  121. DO_REST_ALIGNED_UP(d,s,n);
  122. }
  123. static inline void __memcpy_aligned_dn (unsigned long d, unsigned long s,
  124. long n)
  125. {
  126. s += n;
  127. d += n;
  128. ALIGN_DEST_TO8_DN(d,s,n);
  129. n -= 8;
  130. while (n >= 0) {
  131. unsigned long tmp;
  132. s -= 8;
  133. __asm__("ldq %0,%1":"=r" (tmp):"m" (*(unsigned long *) s));
  134. n -= 8;
  135. d -= 8;
  136. *(unsigned long *) d = tmp;
  137. }
  138. n += 8;
  139. DO_REST_ALIGNED_DN(d,s,n);
  140. }
  141. void * memcpy(void * dest, const void *src, size_t n)
  142. {
  143. if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) {
  144. __memcpy_aligned_up ((unsigned long) dest, (unsigned long) src,
  145.      n);
  146. return dest;
  147. }
  148. __memcpy_unaligned_up ((unsigned long) dest, (unsigned long) src, n);
  149. return dest;
  150. }
  151. /* For backward modules compatibility, define __memcpy.  */
  152. asm("__memcpy = memcpy; .globl __memcpy");