arm64: uaccess: Fix omissions from usercopy whitelist
[sfrench/cifs-2.6.git] / arch / cris / arch-v10 / lib / memset.c
index 42c1101043a3229481acf644ad947d0f427083dc..c94ea9b3ec298a40acb9837d184917bebcc9d841 100644 (file)
-/*#************************************************************************#*/
-/*#-------------------------------------------------------------------------*/
-/*#                                                                         */
-/*# FUNCTION NAME: memset()                                                 */
-/*#                                                                         */
-/*# PARAMETERS:  void* dst;   Destination address.                          */
-/*#              int     c;   Value of byte to write.                       */
-/*#              int   len;   Number of bytes to write.                     */
-/*#                                                                         */
-/*# RETURNS:     dst.                                                       */
-/*#                                                                         */
-/*# DESCRIPTION: Sets the memory dst of length len bytes to c, as standard. */
-/*#              Framework taken from memcpy.  This routine is              */
-/*#              very sensitive to compiler changes in register allocation. */
-/*#              Should really be rewritten to avoid this problem.          */
-/*#                                                                         */
-/*#-------------------------------------------------------------------------*/
-/*#                                                                         */
-/*# HISTORY                                                                 */
-/*#                                                                         */
-/*# DATE      NAME            CHANGES                                       */
-/*# ----      ----            -------                                       */
-/*# 990713    HP              Tired of watching this function (or           */
-/*#                           really, the nonoptimized generic              */
-/*#                           implementation) take up 90% of simulator      */
-/*#                           output.  Measurements needed.                 */
-/*#                                                                         */
-/*#-------------------------------------------------------------------------*/
-
-#include <linux/types.h>
-
-/* No, there's no macro saying 12*4, since it is "hard" to get it into
-   the asm in a good way.  Thus better to expose the problem everywhere.
-   */
-
-/* Assuming 1 cycle per dword written or read (ok, not really true), and
-   one per instruction, then 43+3*(n/48-1) <= 24+24*(n/48-1)
-   so n >= 45.7; n >= 0.9; we win on the first full 48-byte block to set. */
-
-#define ZERO_BLOCK_SIZE (1*12*4)
-
-void *memset(void *pdst,
-             int c,
-             size_t plen)
+/* A memset for CRIS.
+   Copyright (C) 1999-2005 Axis Communications.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+   2. Neither the name of Axis Communications nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY AXIS COMMUNICATIONS AND ITS CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AXIS
+   COMMUNICATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+   IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+   POSSIBILITY OF SUCH DAMAGE.  */
+
+/* FIXME: This file should really only be used for reference, as the
+   result is somewhat depending on gcc generating what we expect rather
+   than what we describe.  An assembly file should be used instead.  */
+
+/* Note the multiple occurrence of the expression "12*4", including the
+   asm.  It is hard to get it into the asm in a good way.  Thus better to
+   expose the problem everywhere: no macro.  */
+
+/* Assuming one cycle per dword written or read (ok, not really true; the
+   world is not ideal), and one cycle per instruction, then 43+3*(n/48-1)
+   <= 24+24*(n/48-1) so n >= 45.7; n >= 0.9; we win on the first full
+   48-byte block to set.  */
+
+#define MEMSET_BY_BLOCK_THRESHOLD (1 * 48)
+
+/* No name ambiguities in this file.  */
+__asm__ (".syntax no_register_prefix");
+
+void *memset(void *pdst, int c, unsigned int plen)
 {
-  /* Ok.  Now we want the parameters put in special registers.
-     Make sure the compiler is able to make something useful of this. */
+  /* Now we want the parameters in special registers.  Make sure the
+     compiler does something usable with this.  */
 
   register char *return_dst __asm__ ("r10") = pdst;
   register int n __asm__ ("r12") = plen;
   register int lc __asm__ ("r11") = c;
 
-  /* Most apps use memset sanely.  Only those memsetting about 3..4
-     bytes or less get penalized compared to the generic implementation
-     - and that's not really sane use. */
+  /* Most apps use memset sanely.  Memsetting about 3..4 bytes or less get
+     penalized here compared to the generic implementation.  */
 
-  /* Ugh.  This is fragile at best.  Check with newer GCC releases, if
-     they compile cascaded "x |= x << 8" sanely! */
-  __asm__("movu.b %0,$r13\n\t"
-          "lslq 8,$r13\n\t"
-         "move.b %0,$r13\n\t"
-         "move.d $r13,%0\n\t"
-         "lslq 16,$r13\n\t"
-         "or.d $r13,%0"
-          : "=r" (lc) : "0" (lc) : "r13");
+  /* This is fragile performancewise at best.  Check with newer GCC
+     releases, if they compile cascaded "x |= x << 8" to sane code.  */
+  __asm__("movu.b %0,r13                                               \n\
+          lslq 8,r13                                                   \n\
+          move.b %0,r13                                                \n\
+          move.d r13,%0                                                \n\
+          lslq 16,r13                                                  \n\
+          or.d r13,%0"
+          : "=r" (lc)          /* Inputs.  */
+         : "0" (lc)            /* Outputs.  */
+         : "r13");             /* Trash.  */
 
   {
     register char *dst __asm__ ("r13") = pdst;
 
-  /* This is NONPORTABLE, but since this whole routine is     */
-  /* grossly nonportable that doesn't matter.                 */
+    if (((unsigned long) pdst & 3) != 0
+       /* Oops! n = 0 must be a valid call, regardless of alignment.  */
+       && n >= 3)
+      {
+       if ((unsigned long) dst & 1)
+         {
+           *dst = (char) lc;
+           n--;
+           dst++;
+         }
 
-  if (((unsigned long) pdst & 3) != 0
-     /* Oops! n=0 must be a legal call, regardless of alignment. */
-      && n >= 3)
-  {
-    if ((unsigned long)dst & 1)
-    {
-      *dst = (char) lc;
-      n--;
-      dst++;
-    }
-
-    if ((unsigned long)dst & 2)
-    {
-      *(short *)dst = lc;
-      n -= 2;
-      dst += 2;
-    }
-  }
+       if ((unsigned long) dst & 2)
+         {
+           *(short *) dst = lc;
+           n -= 2;
+           dst += 2;
+         }
+      }
 
-  /* Now the fun part.  For the threshold value of this, check the equation
-     above. */
-  /* Decide which copying method to use. */
-  if (n >= ZERO_BLOCK_SIZE)
-  {
-    /* For large copies we use 'movem' */
-
-  /* It is not optimal to tell the compiler about clobbering any
-     registers; that will move the saving/restoring of those registers
-     to the function prologue/epilogue, and make non-movem sizes
-     suboptimal.
-
-      This method is not foolproof; it assumes that the "asm reg"
-     declarations at the beginning of the function really are used
-     here (beware: they may be moved to temporary registers).
-      This way, we do not have to save/move the registers around into
-     temporaries; we can safely use them straight away.
-
-      If you want to check that the allocation was right; then
-      check the equalities in the first comment.  It should say
-      "r13=r13, r12=r12, r11=r11" */
-    __asm__ volatile ("\n\
-       ;; Check that the following is true (same register names on     \n\
-       ;; both sides of equal sign, as in r8=r8):                      \n\
-       ;; %0=r13, %1=r12, %4=r11                                       \n\
-       ;;                                                              \n\
-       ;; Save the registers we'll clobber in the movem process        \n\
-       ;; on the stack.  Don't mention them to gcc, it will only be    \n\
-       ;; upset.                                                       \n\
-       subq    11*4,$sp                                                \n\
-       movem   $r10,[$sp]                                              \n\
+    /* Decide which setting method to use.  */
+    if (n >= MEMSET_BY_BLOCK_THRESHOLD)
+      {
+       /* It is not optimal to tell the compiler about clobbering any
+          registers; that will move the saving/restoring of those registers
+          to the function prologue/epilogue, and make non-block sizes
+          suboptimal.  */
+       __asm__ volatile
+         ("\
+          ;; GCC does promise correct register allocations, but let's  \n\
+          ;; make sure it keeps its promises.                          \n\
+          .ifnc %0-%1-%4,$r13-$r12-$r11                                \n\
+          .error \"GCC reg alloc bug: %0-%1-%4 != $r13-$r12-$r11\"     \n\
+          .endif                                                       \n\
+                                                                       \n\
+          ;; Save the registers we'll clobber in the movem process     \n\
+          ;; on the stack.  Don't mention them to gcc, it will only be \n\
+          ;; upset.                                                    \n\
+          subq    11*4,sp                                              \n\
+          movem   r10,[sp]                                             \n\
                                                                        \n\
-       move.d  $r11,$r0                                                \n\
-       move.d  $r11,$r1                                                \n\
-       move.d  $r11,$r2                                                \n\
-       move.d  $r11,$r3                                                \n\
-       move.d  $r11,$r4                                                \n\
-       move.d  $r11,$r5                                                \n\
-       move.d  $r11,$r6                                                \n\
-       move.d  $r11,$r7                                                \n\
-       move.d  $r11,$r8                                                \n\
-       move.d  $r11,$r9                                                \n\
-       move.d  $r11,$r10                                               \n\
+          move.d  r11,r0                                               \n\
+          move.d  r11,r1                                               \n\
+          move.d  r11,r2                                               \n\
+          move.d  r11,r3                                               \n\
+          move.d  r11,r4                                               \n\
+          move.d  r11,r5                                               \n\
+          move.d  r11,r6                                               \n\
+          move.d  r11,r7                                               \n\
+          move.d  r11,r8                                               \n\
+          move.d  r11,r9                                               \n\
+          move.d  r11,r10                                              \n\
                                                                        \n\
-       ;; Now we've got this:                                          \n\
-       ;; r13 - dst                                                    \n\
-       ;; r12 - n                                                      \n\
+          ;; Now we've got this:                                       \n\
+          ;; r13 - dst                                                 \n\
+          ;; r12 - n                                                   \n\
                                                                        \n\
-       ;; Update n for the first loop                                  \n\
-       subq    12*4,$r12                                               \n\
+          ;; Update n for the first loop                               \n\
+          subq    12*4,r12                                             \n\
 0:                                                                     \n\
-       subq    12*4,$r12                                               \n\
-       bge     0b                                                      \n\
-       movem   $r11,[$r13+]                                            \n\
+"
+#ifdef __arch_common_v10_v32
+          /* Cater to branch offset difference between v32 and v10.  We
+             assume the branch below has an 8-bit offset.  */
+"         setf\n"
+#endif
+"         subq   12*4,r12                                              \n\
+          bge     0b                                                   \n\
+          movem        r11,[r13+]                                      \n\
                                                                        \n\
-       addq    12*4,$r12 ;; compensate for last loop underflowing n    \n\
+          ;; Compensate for last loop underflowing n.                  \n\
+          addq   12*4,r12                                              \n\
                                                                        \n\
-       ;; Restore registers from stack                                 \n\
-       movem   [$sp+],$r10"
+          ;; Restore registers from stack.                             \n\
+          movem [sp+],r10"
 
-     /* Outputs */ : "=r" (dst), "=r" (n)
-     /* Inputs */ : "0" (dst), "1" (n), "r" (lc));
+          /* Outputs.  */
+          : "=r" (dst), "=r" (n)
 
-  }
+          /* Inputs.  */
+          : "0" (dst), "1" (n), "r" (lc));
+      }
+
+    /* An ad-hoc unroll, used for 4*12-1..16 bytes. */
+    while (n >= 16)
+      {
+       *(long *) dst = lc; dst += 4;
+       *(long *) dst = lc; dst += 4;
+       *(long *) dst = lc; dst += 4;
+       *(long *) dst = lc; dst += 4;
+       n -= 16;
+      }
 
-    /* Either we directly starts copying, using dword copying
-       in a loop, or we copy as much as possible with 'movem'
-       and then the last block (<44 bytes) is copied here.
-       This will work since 'movem' will have updated src,dst,n. */
-
-    while ( n >= 16 )
-    {
-      *((long*)dst)++ = lc;
-      *((long*)dst)++ = lc;
-      *((long*)dst)++ = lc;
-      *((long*)dst)++ = lc;
-      n -= 16;
-    }
-
-    /* A switch() is definitely the fastest although it takes a LOT of code.
-     * Particularly if you inline code this.
-     */
     switch (n)
-    {
+      {
       case 0:
         break;
+
       case 1:
-        *(char*)dst = (char) lc;
+        *dst = (char) lc;
         break;
+
       case 2:
-        *(short*)dst = (short) lc;
+        *(short *) dst = (short) lc;
         break;
+
       case 3:
-        *((short*)dst)++ = (short) lc;
-        *(char*)dst = (char) lc;
+        *(short *) dst = (short) lc; dst += 2;
+        *dst = (char) lc;
         break;
+
       case 4:
-        *((long*)dst)++ = lc;
+        *(long *) dst = lc;
         break;
+
       case 5:
-        *((long*)dst)++ = lc;
-        *(char*)dst = (char) lc;
+        *(long *) dst = lc; dst += 4;
+        *dst = (char) lc;
         break;
+
       case 6:
-        *((long*)dst)++ = lc;
-        *(short*)dst = (short) lc;
+        *(long *) dst = lc; dst += 4;
+        *(short *) dst = (short) lc;
         break;
+
       case 7:
-        *((long*)dst)++ = lc;
-        *((short*)dst)++ = (short) lc;
-        *(char*)dst = (char) lc;
+        *(long *) dst = lc; dst += 4;
+        *(short *) dst = (short) lc; dst += 2;
+        *dst = (char) lc;
         break;
+
       case 8:
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc;
         break;
+
       case 9:
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
-        *(char*)dst = (char) lc;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc; dst += 4;
+        *dst = (char) lc;
         break;
+
       case 10:
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
-        *(short*)dst = (short) lc;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc; dst += 4;
+        *(short *) dst = (short) lc;
         break;
+
       case 11:
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
-        *((short*)dst)++ = (short) lc;
-        *(char*)dst = (char) lc;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc; dst += 4;
+        *(short *) dst = (short) lc; dst += 2;
+        *dst = (char) lc;
         break;
+
       case 12:
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc;
         break;
+
       case 13:
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
-        *(char*)dst = (char) lc;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc; dst += 4;
+        *dst = (char) lc;
         break;
+
       case 14:
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
-        *(short*)dst = (short) lc;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc; dst += 4;
+        *(short *) dst = (short) lc;
         break;
+
       case 15:
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
-        *((long*)dst)++ = lc;
-        *((short*)dst)++ = (short) lc;
-        *(char*)dst = (char) lc;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc; dst += 4;
+        *(long *) dst = lc; dst += 4;
+        *(short *) dst = (short) lc; dst += 2;
+        *dst = (char) lc;
         break;
-    }
+      }
   }
 
-  return return_dst; /* destination pointer. */
-} /* memset() */
+  return return_dst;
+}