| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * This routine clears to zero a linear memory buffer in user space. | 
|  | 3 | * | 
|  | 4 | * Inputs: | 
|  | 5 | *	in0:	address of buffer | 
|  | 6 | *	in1:	length of buffer in bytes | 
|  | 7 | * Outputs: | 
|  | 8 | *	r8:	number of bytes that didn't get cleared due to a fault | 
|  | 9 | * | 
|  | 10 | * Copyright (C) 1998, 1999, 2001 Hewlett-Packard Co | 
|  | 11 | *	Stephane Eranian <eranian@hpl.hp.com> | 
|  | 12 | */ | 
|  | 13 |  | 
|  | 14 | #include <asm/asmmacro.h> | 
|  | 15 |  | 
|  | 16 | // | 
|  | 17 | // arguments | 
|  | 18 | // | 
|  | 19 | #define buf		r32 | 
|  | 20 | #define len		r33 | 
|  | 21 |  | 
|  | 22 | // | 
|  | 23 | // local registers | 
|  | 24 | // | 
|  | 25 | #define cnt		r16 | 
|  | 26 | #define buf2		r17 | 
|  | 27 | #define saved_lc	r18 | 
|  | 28 | #define saved_pfs	r19 | 
|  | 29 | #define tmp		r20 | 
|  | 30 | #define len2		r21 | 
|  | 31 | #define len3		r22 | 
|  | 32 |  | 
|  | 33 | // | 
|  | 34 | // Theory of operations: | 
|  | 35 | //	- we check whether or not the buffer is small, i.e., less than 17 | 
|  | 36 | //	  in which case we do the byte by byte loop. | 
|  | 37 | // | 
|  | 38 | //	- Otherwise we go progressively from 1 byte store to 8byte store in | 
|  | 39 | //	  the head part, the body is a 16byte store loop and we finish we the | 
|  | 40 | //	  tail for the last 15 bytes. | 
|  | 41 | //	  The good point about this breakdown is that the long buffer handling | 
|  | 42 | //	  contains only 2 branches. | 
|  | 43 | // | 
|  | 44 | //	The reason for not using shifting & masking for both the head and the | 
|  | 45 | //	tail is to stay semantically correct. This routine is not supposed | 
|  | 46 | //	to write bytes outside of the buffer. While most of the time this would | 
|  | 47 | //	be ok, we can't tolerate a mistake. A classical example is the case | 
|  | 48 | //	of multithreaded code were to the extra bytes touched is actually owned | 
|  | 49 | //	by another thread which runs concurrently to ours. Another, less likely, | 
|  | 50 | //	example is with device drivers where reading an I/O mapped location may | 
|  | 51 | //	have side effects (same thing for writing). | 
|  | 52 | // | 
|  | 53 |  | 
|  | 54 | GLOBAL_ENTRY(__do_clear_user) | 
|  | 55 | .prologue | 
|  | 56 | .save ar.pfs, saved_pfs | 
|  | 57 | alloc	saved_pfs=ar.pfs,2,0,0,0 | 
|  | 58 | cmp.eq p6,p0=r0,len		// check for zero length | 
|  | 59 | .save ar.lc, saved_lc | 
|  | 60 | mov saved_lc=ar.lc		// preserve ar.lc (slow) | 
|  | 61 | .body | 
|  | 62 | ;;				// avoid WAW on CFM | 
|  | 63 | adds tmp=-1,len			// br.ctop is repeat/until | 
|  | 64 | mov ret0=len			// return value is length at this point | 
|  | 65 | (p6)	br.ret.spnt.many rp | 
|  | 66 | ;; | 
|  | 67 | cmp.lt p6,p0=16,len		// if len > 16 then long memset | 
|  | 68 | mov ar.lc=tmp			// initialize lc for small count | 
|  | 69 | (p6)	br.cond.dptk .long_do_clear | 
|  | 70 | ;;				// WAR on ar.lc | 
|  | 71 | // | 
|  | 72 | // worst case 16 iterations, avg 8 iterations | 
|  | 73 | // | 
|  | 74 | // We could have played with the predicates to use the extra | 
|  | 75 | // M slot for 2 stores/iteration but the cost the initialization | 
|  | 76 | // the various counters compared to how long the loop is supposed | 
|  | 77 | // to last on average does not make this solution viable. | 
|  | 78 | // | 
|  | 79 | 1: | 
|  | 80 | EX( .Lexit1, st1 [buf]=r0,1 ) | 
|  | 81 | adds len=-1,len			// countdown length using len | 
|  | 82 | br.cloop.dptk 1b | 
|  | 83 | ;;				// avoid RAW on ar.lc | 
|  | 84 | // | 
|  | 85 | // .Lexit4: comes from byte by byte loop | 
|  | 86 | //	    len contains bytes left | 
|  | 87 | .Lexit1: | 
|  | 88 | mov ret0=len			// faster than using ar.lc | 
|  | 89 | mov ar.lc=saved_lc | 
|  | 90 | br.ret.sptk.many rp		// end of short clear_user | 
|  | 91 |  | 
|  | 92 |  | 
|  | 93 | // | 
|  | 94 | // At this point we know we have more than 16 bytes to copy | 
|  | 95 | // so we focus on alignment (no branches required) | 
|  | 96 | // | 
|  | 97 | // The use of len/len2 for countdown of the number of bytes left | 
|  | 98 | // instead of ret0 is due to the fact that the exception code | 
|  | 99 | // changes the values of r8. | 
|  | 100 | // | 
|  | 101 | .long_do_clear: | 
|  | 102 | tbit.nz p6,p0=buf,0		// odd alignment (for long_do_clear) | 
|  | 103 | ;; | 
|  | 104 | EX( .Lexit3, (p6) st1 [buf]=r0,1 )	// 1-byte aligned | 
|  | 105 | (p6)	adds len=-1,len;;		// sync because buf is modified | 
|  | 106 | tbit.nz p6,p0=buf,1 | 
|  | 107 | ;; | 
|  | 108 | EX( .Lexit3, (p6) st2 [buf]=r0,2 )	// 2-byte aligned | 
|  | 109 | (p6)	adds len=-2,len;; | 
|  | 110 | tbit.nz p6,p0=buf,2 | 
|  | 111 | ;; | 
|  | 112 | EX( .Lexit3, (p6) st4 [buf]=r0,4 )	// 4-byte aligned | 
|  | 113 | (p6)	adds len=-4,len;; | 
|  | 114 | tbit.nz p6,p0=buf,3 | 
|  | 115 | ;; | 
|  | 116 | EX( .Lexit3, (p6) st8 [buf]=r0,8 )	// 8-byte aligned | 
|  | 117 | (p6)	adds len=-8,len;; | 
|  | 118 | shr.u cnt=len,4		// number of 128-bit (2x64bit) words | 
|  | 119 | ;; | 
|  | 120 | cmp.eq p6,p0=r0,cnt | 
|  | 121 | adds tmp=-1,cnt | 
|  | 122 | (p6)	br.cond.dpnt .dotail		// we have less than 16 bytes left | 
|  | 123 | ;; | 
|  | 124 | adds buf2=8,buf			// setup second base pointer | 
|  | 125 | mov ar.lc=tmp | 
|  | 126 | ;; | 
|  | 127 |  | 
|  | 128 | // | 
|  | 129 | // 16bytes/iteration core loop | 
|  | 130 | // | 
|  | 131 | // The second store can never generate a fault because | 
|  | 132 | // we come into the loop only when we are 16-byte aligned. | 
|  | 133 | // This means that if we cross a page then it will always be | 
|  | 134 | // in the first store and never in the second. | 
|  | 135 | // | 
|  | 136 | // | 
|  | 137 | // We need to keep track of the remaining length. A possible (optimistic) | 
|  | 138 | // way would be to use ar.lc and derive how many byte were left by | 
|  | 139 | // doing : left= 16*ar.lc + 16.  this would avoid the addition at | 
|  | 140 | // every iteration. | 
|  | 141 | // However we need to keep the synchronization point. A template | 
|  | 142 | // M;;MB does not exist and thus we can keep the addition at no | 
|  | 143 | // extra cycle cost (use a nop slot anyway). It also simplifies the | 
|  | 144 | // (unlikely)  error recovery code | 
|  | 145 | // | 
|  | 146 |  | 
|  | 147 | 2:	EX(.Lexit3, st8 [buf]=r0,16 ) | 
|  | 148 | ;;				// needed to get len correct when error | 
|  | 149 | st8 [buf2]=r0,16 | 
|  | 150 | adds len=-16,len | 
|  | 151 | br.cloop.dptk 2b | 
|  | 152 | ;; | 
|  | 153 | mov ar.lc=saved_lc | 
|  | 154 | // | 
|  | 155 | // tail correction based on len only | 
|  | 156 | // | 
|  | 157 | // We alternate the use of len3,len2 to allow parallelism and correct | 
|  | 158 | // error handling. We also reuse p6/p7 to return correct value. | 
|  | 159 | // The addition of len2/len3 does not cost anything more compared to | 
|  | 160 | // the regular memset as we had empty slots. | 
|  | 161 | // | 
|  | 162 | .dotail: | 
|  | 163 | mov len2=len			// for parallelization of error handling | 
|  | 164 | mov len3=len | 
|  | 165 | tbit.nz p6,p0=len,3 | 
|  | 166 | ;; | 
|  | 167 | EX( .Lexit2, (p6) st8 [buf]=r0,8 )	// at least 8 bytes | 
|  | 168 | (p6)	adds len3=-8,len2 | 
|  | 169 | tbit.nz p7,p6=len,2 | 
|  | 170 | ;; | 
|  | 171 | EX( .Lexit2, (p7) st4 [buf]=r0,4 )	// at least 4 bytes | 
|  | 172 | (p7)	adds len2=-4,len3 | 
|  | 173 | tbit.nz p6,p7=len,1 | 
|  | 174 | ;; | 
|  | 175 | EX( .Lexit2, (p6) st2 [buf]=r0,2 )	// at least 2 bytes | 
|  | 176 | (p6)	adds len3=-2,len2 | 
|  | 177 | tbit.nz p7,p6=len,0 | 
|  | 178 | ;; | 
|  | 179 | EX( .Lexit2, (p7) st1 [buf]=r0 )	// only 1 byte left | 
|  | 180 | mov ret0=r0				// success | 
|  | 181 | br.ret.sptk.many rp			// end of most likely path | 
|  | 182 |  | 
|  | 183 | // | 
|  | 184 | // Outlined error handling code | 
|  | 185 | // | 
|  | 186 |  | 
|  | 187 | // | 
|  | 188 | // .Lexit3: comes from core loop, need restore pr/lc | 
|  | 189 | //	    len contains bytes left | 
|  | 190 | // | 
|  | 191 | // | 
|  | 192 | // .Lexit2: | 
|  | 193 | //	if p6 -> coming from st8 or st2 : len2 contains what's left | 
|  | 194 | //	if p7 -> coming from st4 or st1 : len3 contains what's left | 
|  | 195 | // We must restore lc/pr even though might not have been used. | 
|  | 196 | .Lexit2: | 
|  | 197 | .pred.rel "mutex", p6, p7 | 
|  | 198 | (p6)	mov len=len2 | 
|  | 199 | (p7)	mov len=len3 | 
|  | 200 | ;; | 
|  | 201 | // | 
|  | 202 | // .Lexit4: comes from head, need not restore pr/lc | 
|  | 203 | //	    len contains bytes left | 
|  | 204 | // | 
|  | 205 | .Lexit3: | 
|  | 206 | mov ret0=len | 
|  | 207 | mov ar.lc=saved_lc | 
|  | 208 | br.ret.sptk.many rp | 
|  | 209 | END(__do_clear_user) |