210 lines
		
	
	
	
		
			6 KiB
			
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
		
		
			
		
	
	
			210 lines
		
	
	
	
		
			6 KiB
			
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
|   | /* | ||
|  |  * This routine clears to zero a linear memory buffer in user space. | ||
|  |  * | ||
|  |  * Inputs: | ||
|  |  *	in0:	address of buffer | ||
|  |  *	in1:	length of buffer in bytes | ||
|  |  * Outputs: | ||
|  |  *	r8:	number of bytes that didn't get cleared due to a fault | ||
|  |  * | ||
|  |  * Copyright (C) 1998, 1999, 2001 Hewlett-Packard Co | ||
|  |  *	Stephane Eranian <eranian@hpl.hp.com>
 | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <asm/asmmacro.h> | ||
|  | 
 | ||
|  | // | ||
|  | // arguments | ||
|  | // | ||
|  | #define buf		r32 | ||
|  | #define len		r33 | ||
|  | 
 | ||
|  | // | ||
|  | // local registers | ||
|  | // | ||
|  | #define cnt		r16 | ||
|  | #define buf2		r17 | ||
|  | #define saved_lc	r18 | ||
|  | #define saved_pfs	r19 | ||
|  | #define tmp		r20 | ||
|  | #define len2		r21 | ||
|  | #define len3		r22 | ||
|  | 
 | ||
|  | // | ||
|  | // Theory of operations: | ||
|  | //	- we check whether or not the buffer is small, i.e., less than 17 | ||
|  | //	  in which case we do the byte by byte loop. | ||
|  | // | ||
|  | //	- Otherwise we go progressively from 1 byte store to 8byte store in | ||
|  | //	  the head part, the body is a 16byte store loop and we finish we the | ||
|  | //	  tail for the last 15 bytes. | ||
|  | //	  The good point about this breakdown is that the long buffer handling | ||
|  | //	  contains only 2 branches. | ||
|  | // | ||
|  | //	The reason for not using shifting & masking for both the head and the | ||
|  | //	tail is to stay semantically correct. This routine is not supposed | ||
|  | //	to write bytes outside of the buffer. While most of the time this would | ||
|  | //	be ok, we can't tolerate a mistake. A classical example is the case | ||
|  | //	of multithreaded code were to the extra bytes touched is actually owned | ||
|  | //	by another thread which runs concurrently to ours. Another, less likely, | ||
|  | //	example is with device drivers where reading an I/O mapped location may | ||
|  | //	have side effects (same thing for writing). | ||
|  | // | ||
|  | 
 | ||
|  | GLOBAL_ENTRY(__do_clear_user) | ||
|  | 	.prologue | ||
|  | 	.save ar.pfs, saved_pfs | ||
|  | 	alloc	saved_pfs=ar.pfs,2,0,0,0 | ||
|  | 	cmp.eq p6,p0=r0,len		// check for zero length | ||
|  | 	.save ar.lc, saved_lc | ||
|  | 	mov saved_lc=ar.lc		// preserve ar.lc (slow) | ||
|  | 	.body | ||
|  | 	;;				// avoid WAW on CFM
 | ||
|  | 	adds tmp=-1,len			// br.ctop is repeat/until | ||
|  | 	mov ret0=len			// return value is length at this point | ||
|  | (p6)	br.ret.spnt.many rp | ||
|  | 	;;
 | ||
|  | 	cmp.lt p6,p0=16,len		// if len > 16 then long memset | ||
|  | 	mov ar.lc=tmp			// initialize lc for small count | ||
|  | (p6)	br.cond.dptk .long_do_clear | ||
|  | 	;;				// WAR on ar.lc
 | ||
|  | 	// | ||
|  | 	// worst case 16 iterations, avg 8 iterations | ||
|  | 	// | ||
|  | 	// We could have played with the predicates to use the extra | ||
|  | 	// M slot for 2 stores/iteration but the cost the initialization | ||
|  | 	// the various counters compared to how long the loop is supposed | ||
|  | 	// to last on average does not make this solution viable. | ||
|  | 	// | ||
|  | 1: | ||
|  | 	EX( .Lexit1, st1 [buf]=r0,1 ) | ||
|  | 	adds len=-1,len			// countdown length using len | ||
|  | 	br.cloop.dptk 1b | ||
|  | 	;;				// avoid RAW on ar.lc
 | ||
|  | 	// | ||
|  | 	// .Lexit4: comes from byte by byte loop | ||
|  | 	//	    len contains bytes left | ||
|  | .Lexit1: | ||
|  | 	mov ret0=len			// faster than using ar.lc | ||
|  | 	mov ar.lc=saved_lc | ||
|  | 	br.ret.sptk.many rp		// end of short clear_user | ||
|  | 
 | ||
|  | 
 | ||
|  | 	// | ||
|  | 	// At this point we know we have more than 16 bytes to copy | ||
|  | 	// so we focus on alignment (no branches required) | ||
|  | 	// | ||
|  | 	// The use of len/len2 for countdown of the number of bytes left | ||
|  | 	// instead of ret0 is due to the fact that the exception code | ||
|  | 	// changes the values of r8. | ||
|  | 	// | ||
|  | .long_do_clear: | ||
|  | 	tbit.nz p6,p0=buf,0		// odd alignment (for long_do_clear) | ||
|  | 	;;
 | ||
|  | 	EX( .Lexit3, (p6) st1 [buf]=r0,1 )	// 1-byte aligned | ||
|  | (p6)	adds len=-1,len;;		// sync because buf is modified
 | ||
|  | 	tbit.nz p6,p0=buf,1 | ||
|  | 	;;
 | ||
|  | 	EX( .Lexit3, (p6) st2 [buf]=r0,2 )	// 2-byte aligned | ||
|  | (p6)	adds len=-2,len;;
 | ||
|  | 	tbit.nz p6,p0=buf,2 | ||
|  | 	;;
 | ||
|  | 	EX( .Lexit3, (p6) st4 [buf]=r0,4 )	// 4-byte aligned | ||
|  | (p6)	adds len=-4,len;;
 | ||
|  | 	tbit.nz p6,p0=buf,3 | ||
|  | 	;;
 | ||
|  | 	EX( .Lexit3, (p6) st8 [buf]=r0,8 )	// 8-byte aligned | ||
|  | (p6)	adds len=-8,len;;
 | ||
|  | 	shr.u cnt=len,4		// number of 128-bit (2x64bit) words | ||
|  | 	;;
 | ||
|  | 	cmp.eq p6,p0=r0,cnt | ||
|  | 	adds tmp=-1,cnt | ||
|  | (p6)	br.cond.dpnt .dotail		// we have less than 16 bytes left | ||
|  | 	;;
 | ||
|  | 	adds buf2=8,buf			// setup second base pointer | ||
|  | 	mov ar.lc=tmp | ||
|  | 	;;
 | ||
|  | 
 | ||
|  | 	// | ||
|  | 	// 16bytes/iteration core loop | ||
|  | 	// | ||
|  | 	// The second store can never generate a fault because | ||
|  | 	// we come into the loop only when we are 16-byte aligned. | ||
|  | 	// This means that if we cross a page then it will always be | ||
|  | 	// in the first store and never in the second. | ||
|  | 	// | ||
|  | 	// | ||
|  | 	// We need to keep track of the remaining length. A possible (optimistic) | ||
|  | 	// way would be to use ar.lc and derive how many byte were left by | ||
|  | 	// doing : left= 16*ar.lc + 16.  this would avoid the addition at | ||
|  | 	// every iteration. | ||
|  | 	// However we need to keep the synchronization point. A template | ||
|  | 	// M;;MB does not exist and thus we can keep the addition at no
 | ||
|  | 	// extra cycle cost (use a nop slot anyway). It also simplifies the | ||
|  | 	// (unlikely)  error recovery code | ||
|  | 	// | ||
|  | 
 | ||
|  | 2:	EX(.Lexit3, st8 [buf]=r0,16 ) | ||
|  | 	;;				// needed to get len correct when error
 | ||
|  | 	st8 [buf2]=r0,16 | ||
|  | 	adds len=-16,len | ||
|  | 	br.cloop.dptk 2b | ||
|  | 	;;
 | ||
|  | 	mov ar.lc=saved_lc | ||
|  | 	// | ||
|  | 	// tail correction based on len only | ||
|  | 	// | ||
|  | 	// We alternate the use of len3,len2 to allow parallelism and correct | ||
|  | 	// error handling. We also reuse p6/p7 to return correct value. | ||
|  | 	// The addition of len2/len3 does not cost anything more compared to | ||
|  | 	// the regular memset as we had empty slots. | ||
|  | 	// | ||
|  | .dotail: | ||
|  | 	mov len2=len			// for parallelization of error handling | ||
|  | 	mov len3=len | ||
|  | 	tbit.nz p6,p0=len,3 | ||
|  | 	;;
 | ||
|  | 	EX( .Lexit2, (p6) st8 [buf]=r0,8 )	// at least 8 bytes | ||
|  | (p6)	adds len3=-8,len2 | ||
|  | 	tbit.nz p7,p6=len,2 | ||
|  | 	;;
 | ||
|  | 	EX( .Lexit2, (p7) st4 [buf]=r0,4 )	// at least 4 bytes | ||
|  | (p7)	adds len2=-4,len3 | ||
|  | 	tbit.nz p6,p7=len,1 | ||
|  | 	;;
 | ||
|  | 	EX( .Lexit2, (p6) st2 [buf]=r0,2 )	// at least 2 bytes | ||
|  | (p6)	adds len3=-2,len2 | ||
|  | 	tbit.nz p7,p6=len,0 | ||
|  | 	;;
 | ||
|  | 	EX( .Lexit2, (p7) st1 [buf]=r0 )	// only 1 byte left | ||
|  | 	mov ret0=r0				// success | ||
|  | 	br.ret.sptk.many rp			// end of most likely path | ||
|  | 
 | ||
|  | 	// | ||
|  | 	// Outlined error handling code | ||
|  | 	// | ||
|  | 
 | ||
|  | 	// | ||
|  | 	// .Lexit3: comes from core loop, need restore pr/lc | ||
|  | 	//	    len contains bytes left | ||
|  | 	// | ||
|  | 	// | ||
|  | 	// .Lexit2: | ||
|  | 	//	if p6 -> coming from st8 or st2 : len2 contains what's left | ||
|  | 	//	if p7 -> coming from st4 or st1 : len3 contains what's left | ||
|  | 	// We must restore lc/pr even though might not have been used. | ||
|  | .Lexit2: | ||
|  | 	.pred.rel "mutex", p6, p7 | ||
|  | (p6)	mov len=len2 | ||
|  | (p7)	mov len=len3 | ||
|  | 	;;
 | ||
|  | 	// | ||
|  | 	// .Lexit4: comes from head, need not restore pr/lc | ||
|  | 	//	    len contains bytes left | ||
|  | 	// | ||
|  | .Lexit3: | ||
|  | 	mov ret0=len | ||
|  | 	mov ar.lc=saved_lc | ||
|  | 	br.ret.sptk.many rp | ||
|  | END(__do_clear_user) |