mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-10-26 00:57:39 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			282 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| /* strchr (str, ch) -- Return pointer to first occurrence of CH in STR.
 | |
|    For Motorola 68000.
 | |
|    Copyright (C) 1999-2015 Free Software Foundation, Inc.
 | |
|    This file is part of the GNU C Library.
 | |
|    Contributed by Andreas Schwab <schwab@gnu.org>.
 | |
| 
 | |
|    The GNU C Library is free software; you can redistribute it and/or
 | |
|    modify it under the terms of the GNU Lesser General Public
 | |
|    License as published by the Free Software Foundation; either
 | |
|    version 2.1 of the License, or (at your option) any later version.
 | |
| 
 | |
|    The GNU C Library is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|    Lesser General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU Lesser General Public
 | |
|    License along with the GNU C Library.  If not, see
 | |
|    <http://www.gnu.org/licenses/>.  */
 | |
| 
 | |
| #include <sysdep.h>
 | |
| #include "asm-syntax.h"
 | |
| 
 | |
| 	TEXT
 | |
| ENTRY(strchr)
 | |
| 	/* Save the callee-saved registers we use.  */
 | |
| 	movel	R(d2),MEM_PREDEC(sp)
 | |
| 	cfi_adjust_cfa_offset (4)
 | |
| 	movel	R(d3),MEM_PREDEC(sp)
 | |
| 	cfi_adjust_cfa_offset (4)
 | |
| 	cfi_rel_offset (R(d2),4)
 | |
| 	cfi_rel_offset (R(d3),0)
 | |
| 
 | |
| 	/* Get string pointer and character.  */
 | |
| 	movel	MEM_DISP(sp,12),R(a0)
 | |
| 	moveb	MEM_DISP(sp,19),R(d0)
 | |
| 
 | |
| 	/* Distribute the character to all bytes of a longword.  */
 | |
| 	movel	R(d0),R(d1)
 | |
| 	lsll	#8,R(d1)
 | |
| 	moveb	R(d0),R(d1)
 | |
| 	movel	R(d1),R(d0)
 | |
| 	swap	R(d0)
 | |
| 	movew	R(d1),R(d0)
 | |
| 
 | |
| 	/* First search for the character one byte at a time until the
 | |
| 	   pointer is aligned to a longword boundary.  */
 | |
| 	movel	R(a0),R(d1)
 | |
| #ifdef __mcoldfire__
 | |
| 	andl	#3,R(d1)
 | |
| #else
 | |
| 	andw	#3,R(d1)
 | |
| #endif
 | |
| 	beq	L(L1)
 | |
| 	moveb	MEM(a0),R(d2)
 | |
| 	cmpb	R(d0),R(d2)
 | |
| 	beq	L(L9)
 | |
| 	tstb	R(d2)
 | |
| 	beq	L(L3)
 | |
| 	addql	#1,R(a0)
 | |
| 
 | |
| #ifdef __mcoldfire__
 | |
| 	subql	#3,R(d1)
 | |
| #else
 | |
| 	subqw	#3,R(d1)
 | |
| #endif
 | |
| 	beq	L(L1)
 | |
| 	moveb	MEM(a0),R(d2)
 | |
| 	cmpb	R(d0),R(d2)
 | |
| 	beq	L(L9)
 | |
| 	tstb	R(d2)
 | |
| 	beq	L(L3)
 | |
| 	addql	#1,R(a0)
 | |
| 
 | |
| #ifdef __mcoldfire__
 | |
| 	addql	#1,R(d1)
 | |
| #else
 | |
| 	addqw	#1,R(d1)
 | |
| #endif
 | |
| 	beq	L(L1)
 | |
| 	moveb	MEM(a0),R(d2)
 | |
| 	cmpb	R(d0),R(d2)
 | |
| 	beq	L(L9)
 | |
| 	tstb	R(d2)
 | |
| 	beq	L(L3)
 | |
| 	addql	#1,R(a0)
 | |
| 
 | |
| L(L1:)
 | |
| 	/* Load the magic bits.  Unlike the generic implementation we can
 | |
| 	   use the carry bit as the fourth hole.  */
 | |
| 	movel	#0xfefefeff,R(d3)
 | |
| 
 | |
|       /* We exit the loop if adding MAGIC_BITS to LONGWORD fails to
 | |
| 	 change any of the hole bits of LONGWORD.
 | |
| 
 | |
| 	 1) Is this safe?  Will it catch all the zero bytes?
 | |
| 	 Suppose there is a byte with all zeros.  Any carry bits
 | |
| 	 propagating from its left will fall into the hole at its
 | |
| 	 least significant bit and stop.  Since there will be no
 | |
| 	 carry from its most significant bit, the LSB of the
 | |
| 	 byte to the left will be unchanged, and the zero will be
 | |
| 	 detected.
 | |
| 
 | |
| 	 2) Is this worthwhile?  Will it ignore everything except
 | |
| 	 zero bytes?  Suppose every byte of LONGWORD has a bit set
 | |
| 	 somewhere.  There will be a carry into bit 8.	If bit 8
 | |
| 	 is set, this will carry into bit 16.  If bit 8 is clear,
 | |
| 	 one of bits 9-15 must be set, so there will be a carry
 | |
| 	 into bit 16.  Similarly, there will be a carry into bit
 | |
| 	 24.  If one of bits 24-31 is set, there will be a carry
 | |
| 	 into bit 32 (=carry flag), so all of the hole bits will
 | |
| 	 be changed.
 | |
| 
 | |
| 	 3) But wait!  Aren't we looking for C, not zero?
 | |
| 	 Good point.  So what we do is XOR LONGWORD with a longword,
 | |
| 	 each of whose bytes is C.  This turns each byte that is C
 | |
| 	 into a zero.  */
 | |
| 
 | |
| L(L2:)
 | |
| 	/* Get the longword in question.  */
 | |
| 	movel	MEM_POSTINC(a0),R(d1)
 | |
| 	/* XOR with the byte we search for.  */
 | |
| 	eorl	R(d0),R(d1)
 | |
| 
 | |
| 	/* Add the magic value.  We get carry bits reported for each byte
 | |
| 	   which is not C.  */
 | |
| 	movel	R(d3),R(d2)
 | |
| 	addl	R(d1),R(d2)
 | |
| 
 | |
| 	/* Check the fourth carry bit before it is clobbered by the next
 | |
| 	   XOR.  If it is not set we have a hit.  */
 | |
| 	bcc	L(L8)
 | |
| 
 | |
| 	/* We are only interested in carry bits that change due to the
 | |
| 	   previous add, so remove original bits.  */
 | |
| 	eorl	R(d1),R(d2)
 | |
| 
 | |
| 	/* Now test for the other three overflow bits.
 | |
| 	   Set all non-carry bits.  */
 | |
| 	orl	R(d3),R(d2)
 | |
| 	/* Add 1 to get zero if all carry bits were set.  */
 | |
| 	addql	#1,R(d2)
 | |
| 
 | |
| 	/* If we don't get zero then at least one byte of the word equals
 | |
| 	   C.  */
 | |
| 	bne	L(L8)
 | |
| 
 | |
| 	/* Next look for a NUL byte.
 | |
| 	   Restore original longword without reload.  */
 | |
| 	eorl	R(d0),R(d1)
 | |
| 	/* Add the magic value.  We get carry bits reported for each byte
 | |
| 	   which is not NUL.  */
 | |
| 	movel	R(d3),R(d2)
 | |
| 	addl	R(d1),R(d2)
 | |
| 
 | |
| 	/* Check the fourth carry bit before it is clobbered by the next
 | |
| 	   XOR.  If it is not set we have a hit, and return NULL.  */
 | |
| 	bcc	L(L3)
 | |
| 
 | |
| 	/* We are only interested in carry bits that change due to the
 | |
| 	   previous add, so remove original bits.  */
 | |
| 	eorl	R(d1),R(d2)
 | |
| 
 | |
| 	/* Now test for the other three overflow bits.
 | |
| 	   Set all non-carry bits.  */
 | |
| 	orl	R(d3),R(d2)
 | |
| 	/* Add 1 to get zero if all carry bits were set.  */
 | |
| 	addql	#1,R(d2)
 | |
| 
 | |
| 	/* If we don't get zero then at least one byte of the word was NUL
 | |
| 	   and we return NULL.  Otherwise continue with the next longword.  */
 | |
| 	bne	L(L3)
 | |
| 
 | |
| 	/* Get the longword in question.  */
 | |
| 	movel	MEM_POSTINC(a0),R(d1)
 | |
| 	/* XOR with the byte we search for.  */
 | |
| 	eorl	R(d0),R(d1)
 | |
| 
 | |
| 	/* Add the magic value.  We get carry bits reported for each byte
 | |
| 	   which is not C.  */
 | |
| 	movel	R(d3),R(d2)
 | |
| 	addl	R(d1),R(d2)
 | |
| 
 | |
| 	/* Check the fourth carry bit before it is clobbered by the next
 | |
| 	   XOR.  If it is not set we have a hit.  */
 | |
| 	bcc	L(L8)
 | |
| 
 | |
| 	/* We are only interested in carry bits that change due to the
 | |
| 	   previous add, so remove original bits */
 | |
| 	eorl	R(d1),R(d2)
 | |
| 
 | |
| 	/* Now test for the other three overflow bits.
 | |
| 	   Set all non-carry bits.  */
 | |
| 	orl	R(d3),R(d2)
 | |
| 	/* Add 1 to get zero if all carry bits were set.  */
 | |
| 	addql	#1,R(d2)
 | |
| 
 | |
| 	/* If we don't get zero then at least one byte of the word equals
 | |
| 	   C.  */
 | |
| 	bne	L(L8)
 | |
| 
 | |
| 	/* Next look for a NUL byte.
 | |
| 	   Restore original longword without reload.  */
 | |
| 	eorl	R(d0),R(d1)
 | |
| 	/* Add the magic value.  We get carry bits reported for each byte
 | |
| 	   which is not NUL.  */
 | |
| 	movel	R(d3),R(d2)
 | |
| 	addl	R(d1),R(d2)
 | |
| 
 | |
| 	/* Check the fourth carry bit before it is clobbered by the next
 | |
| 	   XOR.  If it is not set we have a hit, and return NULL.  */
 | |
| 	bcc	L(L3)
 | |
| 
 | |
| 	/* We are only interested in carry bits that change due to the
 | |
| 	   previous add, so remove original bits */
 | |
| 	eorl	R(d1),R(d2)
 | |
| 
 | |
| 	/* Now test for the other three overflow bits.
 | |
| 	   Set all non-carry bits.  */
 | |
| 	orl	R(d3),R(d2)
 | |
| 	/* Add 1 to get zero if all carry bits were set.  */
 | |
| 	addql	#1,R(d2)
 | |
| 
 | |
| 	/* If we don't get zero then at least one byte of the word was NUL
 | |
| 	   and we return NULL.  Otherwise continue with the next longword.  */
 | |
| 	beq	L(L2)
 | |
| 
 | |
| L(L3:)
 | |
| 	/* Return NULL.  */
 | |
| 	clrl	R(d0)
 | |
| 	movel	R(d0),R(a0)
 | |
| 	movel	MEM_POSTINC(sp),R(d3)
 | |
| 	cfi_remember_state
 | |
| 	cfi_adjust_cfa_offset (-4)
 | |
| 	cfi_restore (R(d3))
 | |
| 	movel	MEM_POSTINC(sp),R(d2)
 | |
| 	cfi_adjust_cfa_offset (-4)
 | |
| 	cfi_restore (R(d2))
 | |
| 	rts
 | |
| 
 | |
| 	cfi_restore_state
 | |
| L(L8:)
 | |
| 	/* We have a hit.  Check to see which byte it was.  First
 | |
| 	   compensate for the autoincrement in the loop.  */
 | |
| 	subql	#4,R(a0)
 | |
| 
 | |
| 	moveb	MEM(a0),R(d1)
 | |
| 	cmpb	R(d0),R(d1)
 | |
| 	beq	L(L9)
 | |
| 	tstb	R(d1)
 | |
| 	beq	L(L3)
 | |
| 	addql	#1,R(a0)
 | |
| 
 | |
| 	moveb	MEM(a0),R(d1)
 | |
| 	cmpb	R(d0),R(d1)
 | |
| 	beq	L(L9)
 | |
| 	tstb	R(d1)
 | |
| 	beq	L(L3)
 | |
| 	addql	#1,R(a0)
 | |
| 
 | |
| 	moveb	MEM(a0),R(d1)
 | |
| 	cmpb	R(d0),R(d1)
 | |
| 	beq	L(L9)
 | |
| 	tstb	R(d1)
 | |
| 	beq	L(L3)
 | |
| 	addql	#1,R(a0)
 | |
| 
 | |
| 	/* Otherwise the fourth byte must equal C.  */
 | |
| L(L9:)
 | |
| 	movel	R(a0),R(d0)
 | |
| 	movel	MEM_POSTINC(sp),R(d3)
 | |
| 	cfi_adjust_cfa_offset (-4)
 | |
| 	cfi_restore (R(d3))
 | |
| 	movel	MEM_POSTINC(sp),R(d2)
 | |
| 	cfi_adjust_cfa_offset (-4)
 | |
| 	cfi_restore (R(d2))
 | |
| 	rts
 | |
| END(strchr)
 | |
| 
 | |
| weak_alias (strchr, index)
 | |
| libc_hidden_builtin_def (strchr)
 |