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