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

嵌入式Linux

开发平台:

Unix_Linux

  1. /*#************************************************************************#*/
  2. /*#-------------------------------------------------------------------------*/
  3. /*#                                                                         */
  4. /*# FUNCTION NAME: memset()                                                 */
  5. /*#                                                                         */
  6. /*# PARAMETERS:  void* dst;   Destination address.                          */
  7. /*#              int     c;   Value of byte to write.                       */
  8. /*#              int   len;   Number of bytes to write.                     */
  9. /*#                                                                         */
  10. /*# RETURNS:     dst.                                                       */
  11. /*#                                                                         */
  12. /*# DESCRIPTION: Sets the memory dst of length len bytes to c, as standard. */
  13. /*#              Framework taken from memcpy.  This routine is              */
  14. /*#              very sensitive to compiler changes in register allocation. */
  15. /*#              Should really be rewritten to avoid this problem.          */
  16. /*#                                                                         */
  17. /*#-------------------------------------------------------------------------*/
  18. /*#                                                                         */
  19. /*# HISTORY                                                                 */
  20. /*#                                                                         */
  21. /*# DATE      NAME            CHANGES                                       */
  22. /*# ----      ----            -------                                       */
  23. /*# 990713    HP              Tired of watching this function (or           */
  24. /*#                           really, the nonoptimized generic              */
  25. /*#                           implementation) take up 90% of simulator      */
  26. /*#                           output.  Measurements needed.                 */
  27. /*#                                                                         */
  28. /*#-------------------------------------------------------------------------*/
  29. #include <linux/types.h>
  30. /* No, there's no macro saying 12*4, since it is "hard" to get it into
  31.    the asm in a good way.  Thus better to expose the problem everywhere.
  32.    */
  33. /* Assuming 1 cycle per dword written or read (ok, not really true), and
  34.    one per instruction, then 43+3*(n/48-1) <= 24+24*(n/48-1)
  35.    so n >= 45.7; n >= 0.9; we win on the first full 48-byte block to set. */
  36. #define ZERO_BLOCK_SIZE (1*12*4)
  37. void *memset(void *pdst,
  38.              int c,
  39.              size_t plen)
  40. {
  41.   /* Ok.  Now we want the parameters put in special registers.
  42.      Make sure the compiler is able to make something useful of this. */
  43.   register char *return_dst __asm__ ("r10") = pdst;
  44.   register int n __asm__ ("r12") = plen;
  45.   register int lc __asm__ ("r11") = c;
  46.   /* Most apps use memset sanely.  Only those memsetting about 3..4
  47.      bytes or less get penalized compared to the generic implementation
  48.      - and that's not really sane use. */
  49.   /* Ugh.  This is fragile at best.  Check with newer GCC releases, if
  50.      they compile cascaded "x |= x << 8" sanely! */
  51.   __asm__("movu.b %0,$r13nt"
  52.           "lslq 8,$r13nt"
  53.   "move.b %0,$r13nt"
  54.   "move.d $r13,%0nt"
  55.   "lslq 16,$r13nt"
  56.   "or.d $r13,%0"
  57.           : "=r" (lc) : "0" (lc) : "r13");
  58.   {
  59.     register char *dst __asm__ ("r13") = pdst;
  60.  
  61.   /* This is NONPORTABLE, but since this whole routine is     */
  62.   /* grossly nonportable that doesn't matter.                 */
  63.   if (((unsigned long) pdst & 3) != 0
  64.      /* Oops! n=0 must be a legal call, regardless of alignment. */
  65.       && n >= 3)
  66.   {
  67.     if ((unsigned long)dst & 1)
  68.     {
  69.       *dst = (char) lc;
  70.       n--;
  71.       dst++;
  72.     }
  73.     if ((unsigned long)dst & 2)
  74.     {
  75.       *(short *)dst = lc;
  76.       n -= 2;
  77.       dst += 2;
  78.     }
  79.   }
  80.   /* Now the fun part.  For the threshold value of this, check the equation
  81.      above. */
  82.   /* Decide which copying method to use. */
  83.   if (n >= ZERO_BLOCK_SIZE)
  84.   {
  85.     /* For large copies we use 'movem' */
  86.   /* It is not optimal to tell the compiler about clobbering any
  87.      registers; that will move the saving/restoring of those registers
  88.      to the function prologue/epilogue, and make non-movem sizes
  89.      suboptimal.
  90.       This method is not foolproof; it assumes that the "asm reg"
  91.      declarations at the beginning of the function really are used
  92.      here (beware: they may be moved to temporary registers).
  93.       This way, we do not have to save/move the registers around into
  94.      temporaries; we can safely use them straight away.
  95.       If you want to check that the allocation was right; then
  96.       check the equalities in the first comment.  It should say
  97.       "r13=r13, r12=r12, r11=r11" */
  98.     __asm__ volatile ("
  99.         ;; Check that the following is true (same register names on
  100.         ;; both sides of equal sign, as in r8=r8):
  101.         ;; %0=r13, %1=r12, %4=r11
  102.         ;;
  103. ;; Save the registers we'll clobber in the movem process
  104. ;; on the stack.  Don't mention them to gcc, it will only be
  105. ;; upset.
  106. subq  11*4,$sp
  107.         movem   $r10,[$sp]
  108.         move.d  $r11,$r0
  109.         move.d  $r11,$r1
  110.         move.d  $r11,$r2
  111.         move.d  $r11,$r3
  112.         move.d  $r11,$r4
  113.         move.d  $r11,$r5
  114.         move.d  $r11,$r6
  115.         move.d  $r11,$r7
  116.         move.d  $r11,$r8
  117.         move.d  $r11,$r9
  118.         move.d  $r11,$r10
  119.         ;; Now we've got this:
  120. ;; r13 - dst
  121. ;; r12 - n
  122.         ;; Update n for the first loop
  123.         subq    12*4,$r12
  124. 0:
  125.         subq   12*4,$r12
  126.         bge     0b
  127. movem $r11,[$r13+]
  128.         addq   12*4,$r12  ;; compensate for last loop underflowing n
  129. ;; Restore registers from stack
  130.         movem [$sp+],$r10" 
  131.      /* Outputs */ : "=r" (dst), "=r" (n)
  132.      /* Inputs */ : "0" (dst), "1" (n), "r" (lc));
  133.     
  134.   }
  135.     /* Either we directly starts copying, using dword copying
  136.        in a loop, or we copy as much as possible with 'movem' 
  137.        and then the last block (<44 bytes) is copied here.
  138.        This will work since 'movem' will have updated src,dst,n. */
  139.     while ( n >= 16 )
  140.     {
  141.       *((long*)dst)++ = lc;
  142.       *((long*)dst)++ = lc;
  143.       *((long*)dst)++ = lc;
  144.       *((long*)dst)++ = lc;
  145.       n -= 16;
  146.     }
  147.     /* A switch() is definitely the fastest although it takes a LOT of code.
  148.      * Particularly if you inline code this.
  149.      */
  150.     switch (n)
  151.     {
  152.       case 0:
  153.         break;
  154.       case 1:
  155.         *(char*)dst = (char) lc;
  156.         break;
  157.       case 2:
  158.         *(short*)dst = (short) lc;
  159.         break;
  160.       case 3:
  161.         *((short*)dst)++ = (short) lc;
  162.         *(char*)dst = (char) lc;
  163.         break;
  164.       case 4:
  165.         *((long*)dst)++ = lc;
  166.         break;
  167.       case 5:
  168.         *((long*)dst)++ = lc;
  169.         *(char*)dst = (char) lc;
  170.         break;
  171.       case 6:
  172.         *((long*)dst)++ = lc;
  173.         *(short*)dst = (short) lc;
  174.         break;
  175.       case 7:
  176.         *((long*)dst)++ = lc;
  177.         *((short*)dst)++ = (short) lc;
  178.         *(char*)dst = (char) lc;
  179.         break;
  180.       case 8:
  181.         *((long*)dst)++ = lc;
  182.         *((long*)dst)++ = lc;
  183.         break;
  184.       case 9:
  185.         *((long*)dst)++ = lc;
  186.         *((long*)dst)++ = lc;
  187.         *(char*)dst = (char) lc;
  188.         break;
  189.       case 10:
  190.         *((long*)dst)++ = lc;
  191.         *((long*)dst)++ = lc;
  192.         *(short*)dst = (short) lc;
  193.         break;
  194.       case 11:
  195.         *((long*)dst)++ = lc;
  196.         *((long*)dst)++ = lc;
  197.         *((short*)dst)++ = (short) lc;
  198.         *(char*)dst = (char) lc;
  199.         break;
  200.       case 12:
  201.         *((long*)dst)++ = lc;
  202.         *((long*)dst)++ = lc;
  203.         *((long*)dst)++ = lc;
  204.         break;
  205.       case 13:
  206.         *((long*)dst)++ = lc;
  207.         *((long*)dst)++ = lc;
  208.         *((long*)dst)++ = lc;
  209.         *(char*)dst = (char) lc;
  210.         break;
  211.       case 14:
  212.         *((long*)dst)++ = lc;
  213.         *((long*)dst)++ = lc;
  214.         *((long*)dst)++ = lc;
  215.         *(short*)dst = (short) lc;
  216.         break;
  217.       case 15:
  218.         *((long*)dst)++ = lc;
  219.         *((long*)dst)++ = lc;
  220.         *((long*)dst)++ = lc;
  221.         *((short*)dst)++ = (short) lc;
  222.         *(char*)dst = (char) lc;
  223.         break;
  224.     }
  225.   }
  226.   return return_dst; /* destination pointer. */
  227. } /* memset() */