1140 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			1140 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| Subject: [PATCH] AVR32-optimized string operations
 | |
| 
 | |
| Add hand-optimized AVR32-specific string operations. Some of them
 | |
| need a bit more testing, though.
 | |
| 
 | |
| ---
 | |
| 
 | |
|  libc/string/avr32/Makefile      |   40 +++++++++++
 | |
|  libc/string/avr32/bcopy.S       |   15 ++++
 | |
|  libc/string/avr32/bzero.S       |   12 +++
 | |
|  libc/string/avr32/memchr.S      |   62 +++++++++++++++++
 | |
|  libc/string/avr32/memcmp.S      |   50 +++++++++++++
 | |
|  libc/string/avr32/memcpy.S      |  110 ++++++++++++++++++++++++++++++
 | |
|  libc/string/avr32/memmove.S     |  114 +++++++++++++++++++++++++++++++
 | |
|  libc/string/avr32/memset.S      |   60 ++++++++++++++++
 | |
|  libc/string/avr32/strcat.S      |   95 ++++++++++++++++++++++++++
 | |
|  libc/string/avr32/strcmp.S      |   80 ++++++++++++++++++++++
 | |
|  libc/string/avr32/strcpy.S      |   63 +++++++++++++++++
 | |
|  libc/string/avr32/stringtest.c  |  144 ++++++++++++++++++++++++++++++++++++++++
 | |
|  libc/string/avr32/strlen.S      |   52 ++++++++++++++
 | |
|  libc/string/avr32/strncpy.S     |   77 +++++++++++++++++++++
 | |
|  libc/string/avr32/test_memcpy.c |   66 ++++++++++++++++++
 | |
|  15 files changed, 1040 insertions(+)
 | |
| 
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/bcopy.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/bcopy.S	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,15 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway
 | |
| + */
 | |
| +
 | |
| +	.text
 | |
| +	.global bcopy
 | |
| +	.type	bcopy, @function
 | |
| +	.align	1
 | |
| +bcopy:
 | |
| +	/* Swap the first two arguments */
 | |
| +	eor	r11, r12
 | |
| +	eor	r12, r11
 | |
| +	eor	r11, r12
 | |
| +	rjmp	__memmove
 | |
| +	.size	bcopy, . - bcopy
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/bzero.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/bzero.S	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,12 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway
 | |
| + */
 | |
| +
 | |
| +	.text
 | |
| +	.global bzero
 | |
| +	.type	bzero, @function
 | |
| +	.align	1
 | |
| +bzero:
 | |
| +	mov	r10, r11
 | |
| +	mov	r11, 0
 | |
| +	rjmp	__memset
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/Makefile
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/Makefile	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,40 @@
 | |
| +# Makefile for uClibc
 | |
| +#
 | |
| +# Copyright (C) 2000-2003 Erik Andersen <andersen@uclibc.org>
 | |
| +#
 | |
| +# This program is free software; you can redistribute it and/or modify it under
 | |
| +# the terms of the GNU Library General Public License as published by the Free
 | |
| +# Software Foundation; either version 2 of the License, or (at your option) any
 | |
| +# later version.
 | |
| +#
 | |
| +# This program 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 Library General Public License for more
 | |
| +# details.
 | |
| +#
 | |
| +# You should have received a copy of the GNU Library General Public License
 | |
| +# along with this program; if not, write to the Free Software Foundation, Inc.,
 | |
| +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 | |
| +
 | |
| +TOPDIR=../../../
 | |
| +include $(TOPDIR)Rules.mak
 | |
| +
 | |
| +SSRC	:= bcopy.S bzero.S memcmp.S memcpy.S memmove.S
 | |
| +SSRC	+= memset.S strcmp.S strlen.S
 | |
| +# memchr.S, strcat.S, strcpy.S, strncpy.S is broken
 | |
| +SOBJS	:= $(patsubst %.S,%.o, $(SSRC))
 | |
| +OBJS	:= $(SOBJS)
 | |
| +
 | |
| +OBJ_LIST:= ../../obj.string.$(TARGET_ARCH)
 | |
| +
 | |
| +all: $(OBJ_LIST)
 | |
| +
 | |
| +$(OBJ_LIST): $(OBJS)
 | |
| +	echo $(addprefix string/$(TARGET_ARCH)/, $(OBJS)) > $@
 | |
| +
 | |
| +$(SOBJS): %.o: %.S
 | |
| +	$(CC) $(ASFLAGS) -c $< -o $@
 | |
| +	$(STRIPTOOL) -x -R .note -R .comment $@
 | |
| +
 | |
| +clean:
 | |
| +	$(RM) *.[oa] *~ core
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/memchr.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/memchr.S	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,62 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway
 | |
| + */
 | |
| +
 | |
| +#define str r12
 | |
| +#define chr r11
 | |
| +#define len r10
 | |
| +
 | |
| +	.text
 | |
| +	.global memchr
 | |
| +	.type	memchr, @function
 | |
| +memchr:
 | |
| +	or	chr, chr, chr << 8
 | |
| +	or	chr, chr, chr << 16
 | |
| +
 | |
| +	mov	r9, str
 | |
| +	andl	r9, 3, COH
 | |
| +	brne	.Lunaligned_str
 | |
| +
 | |
| +1:	sub	len, 4
 | |
| +	brlt	2f
 | |
| +	ld.w	r8, str++
 | |
| +	psub.b	r9, r8, r11
 | |
| +	tnbz	r9
 | |
| +	brne	1b
 | |
| +
 | |
| +	sub	str, 4
 | |
| +	bfextu	r9, r8, 24, 8
 | |
| +	cp.b	r9, r11
 | |
| +	reteq	str
 | |
| +	sub	str, -1
 | |
| +	bfextu	r9, r8, 16, 8
 | |
| +	cp.b	r9, r11
 | |
| +	reteq	str
 | |
| +	sub	str, -1
 | |
| +	bfextu	r9, r8, 8, 8
 | |
| +	cp.b	r9, r11
 | |
| +	reteq	str
 | |
| +	sub	str, -1
 | |
| +	retal	str
 | |
| +
 | |
| +2:	sub	len, -4
 | |
| +	reteq	0
 | |
| +
 | |
| +3:	ld.ub	r8, str++
 | |
| +	cp.w	r8, 0
 | |
| +	reteq	str
 | |
| +	sub	len, 1
 | |
| +	brne	3b
 | |
| +
 | |
| +	retal	0
 | |
| +
 | |
| +.Lunaligned_str:
 | |
| +1:	sub	len, 1
 | |
| +	retlt	0
 | |
| +	ld.ub	r8, str++
 | |
| +	cp.b	r8, r11
 | |
| +	reteq	str
 | |
| +	sub	r9, 1
 | |
| +	brge	1b
 | |
| +
 | |
| +	rjmp	.Laligned_search
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/memcmp.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/memcmp.S	2006-10-20 10:42:09.000000000 +0200
 | |
| @@ -0,0 +1,50 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway.
 | |
| + */
 | |
| +
 | |
| +#define s1 r12
 | |
| +#define s2 r11
 | |
| +#define len r10
 | |
| +
 | |
| +	.text
 | |
| +	.global memcmp
 | |
| +	.type	memcmp, @function
 | |
| +	.align	1
 | |
| +memcmp:
 | |
| +	sub	len, 4
 | |
| +	brlt	.Lless_than_4
 | |
| +
 | |
| +1:	ld.w	r8, s1++
 | |
| +	ld.w	r9, s2++
 | |
| +	cp.w	r8, r9
 | |
| +	brne	.Lfound_word
 | |
| +	sub	len, 4
 | |
| +	brge	1b
 | |
| +
 | |
| +.Lless_than_4:
 | |
| +	sub	len, -4
 | |
| +	reteq	0
 | |
| +
 | |
| +1:	ld.ub	r8, s1++
 | |
| +	ld.ub	r9, s2++
 | |
| +	sub	r8, r9
 | |
| +	retne	r8
 | |
| +	sub	len, 1
 | |
| +	brgt	1b
 | |
| +
 | |
| +	retal	0
 | |
| +
 | |
| +.Lfound_word:
 | |
| +	psub.b	r9, r8, r9
 | |
| +	bfextu	r8, r9, 24, 8
 | |
| +	retne	r8
 | |
| +	bfextu	r8, r9, 16, 8
 | |
| +	retne	r8
 | |
| +	bfextu	r8, r9, 8, 8
 | |
| +	retne	r8
 | |
| +	retal	r9
 | |
| +
 | |
| +	.size	memcmp, . - memcmp
 | |
| +
 | |
| +	.weak	bcmp
 | |
| +	bcmp = memcmp
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/memcpy.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/memcpy.S	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,110 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway
 | |
| + */
 | |
| +
 | |
| +/* Don't use r12 as dst since we must return it unmodified */
 | |
| +#define dst r9
 | |
| +#define src r11
 | |
| +#define len r10
 | |
| +
 | |
| +	.text
 | |
| +	.global	memcpy
 | |
| +	.type	memcpy, @function
 | |
| +
 | |
| +	.global	__memcpy
 | |
| +	.hidden	__memcpy
 | |
| +	.type	__memcpy, @function
 | |
| +memcpy:
 | |
| +__memcpy:
 | |
| +	pref	src[0]
 | |
| +	mov	dst, r12
 | |
| +
 | |
| +	/* If we have less than 32 bytes, don't do anything fancy */
 | |
| +	cp.w	len, 32
 | |
| +	brge	.Lmore_than_31
 | |
| +
 | |
| +	sub	len, 1
 | |
| +	retlt	r12
 | |
| +1:	ld.ub	r8, src++
 | |
| +	st.b	dst++, r8
 | |
| +	sub	len, 1
 | |
| +	brge	1b
 | |
| +	retal	r12
 | |
| +
 | |
| +.Lmore_than_31:
 | |
| +	pushm	r0-r7, lr
 | |
| +
 | |
| +	/* Check alignment */
 | |
| +	mov	r8, src
 | |
| +	andl	r8, 31, COH
 | |
| +	brne	.Lunaligned_src
 | |
| +	mov	r8, dst
 | |
| +	andl	r8, 3, COH
 | |
| +	brne	.Lunaligned_dst
 | |
| +
 | |
| +.Laligned_copy:
 | |
| +	sub	len, 32
 | |
| +	brlt	.Lless_than_32
 | |
| +
 | |
| +1:	/* Copy 32 bytes at a time */
 | |
| +	ldm	src, r0-r7
 | |
| +	sub	src, -32
 | |
| +	stm	dst, r0-r7
 | |
| +	sub	dst, -32
 | |
| +	sub	len, 32
 | |
| +	brge	1b
 | |
| +
 | |
| +.Lless_than_32:
 | |
| +	/* Copy 16 more bytes if possible */
 | |
| +	sub	len, -16
 | |
| +	brlt	.Lless_than_16
 | |
| +	ldm	src, r0-r3
 | |
| +	sub	src, -16
 | |
| +	sub	len, 16
 | |
| +	stm	dst, r0-r3
 | |
| +	sub	dst, -16
 | |
| +
 | |
| +.Lless_than_16:
 | |
| +	/* Do the remaining as byte copies */
 | |
| +	neg	len
 | |
| +	add	pc, pc, len << 2
 | |
| +	.rept	15
 | |
| +	ld.ub	r0, src++
 | |
| +	st.b	dst++, r0
 | |
| +	.endr
 | |
| +
 | |
| +	popm	r0-r7, pc
 | |
| +
 | |
| +.Lunaligned_src:
 | |
| +	/* Make src cacheline-aligned. r8 = (src & 31) */
 | |
| +	rsub	r8, r8, 32
 | |
| +	sub	len, r8
 | |
| +1:	ld.ub	r0, src++
 | |
| +	st.b	dst++, r0
 | |
| +	sub	r8, 1
 | |
| +	brne	1b
 | |
| +
 | |
| +	/* If dst is word-aligned, we're ready to go */
 | |
| +	pref	src[0]
 | |
| +	mov	r8, 3
 | |
| +	tst	dst, r8
 | |
| +	breq	.Laligned_copy
 | |
| +
 | |
| +.Lunaligned_dst:
 | |
| +	/* src is aligned, but dst is not. Expect bad performance */
 | |
| +	sub	len, 4
 | |
| +	brlt	2f
 | |
| +1:	ld.w	r0, src++
 | |
| +	st.w	dst++, r0
 | |
| +	sub	len, 4
 | |
| +	brge	1b
 | |
| +
 | |
| +2:	neg	len
 | |
| +	add	pc, pc, len << 2
 | |
| +	.rept	3
 | |
| +	ld.ub	r0, src++
 | |
| +	st.b	dst++, r0
 | |
| +	.endr
 | |
| +
 | |
| +	popm	r0-r7, pc
 | |
| +	.size	memcpy, . - memcpy
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/memmove.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/memmove.S	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,114 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway
 | |
| + */
 | |
| +
 | |
| +#define dst r12
 | |
| +#define src r11
 | |
| +#define len r10
 | |
| +
 | |
| +	.text
 | |
| +	.global memmove
 | |
| +	.type	memmove, @function
 | |
| +
 | |
| +	.global	__memmove
 | |
| +	.hidden	__memmove
 | |
| +	.type	__memmove, @function
 | |
| +memmove:
 | |
| +__memmove:
 | |
| +	cp.w	src, dst
 | |
| +	brge	__memcpy
 | |
| +
 | |
| +	add	dst, len
 | |
| +	add	src, len
 | |
| +	pref	src[-1]
 | |
| +
 | |
| +	/*
 | |
| +	 * The rest is basically the same as in memcpy.S except that
 | |
| +	 * the direction is reversed.
 | |
| +	 */
 | |
| +	cp.w	len, 32
 | |
| +	brge	.Lmore_than_31
 | |
| +
 | |
| +	sub	len, 1
 | |
| +	retlt	r12
 | |
| +1:	ld.ub	r8, --src
 | |
| +	st.b	--dst, r8
 | |
| +	sub	len, 1
 | |
| +	brge	1b
 | |
| +	retal	r12
 | |
| +
 | |
| +.Lmore_than_31:
 | |
| +	pushm	r0-r7, lr
 | |
| +
 | |
| +	/* Check alignment */
 | |
| +	mov	r8, src
 | |
| +	andl	r8, 31, COH
 | |
| +	brne	.Lunaligned_src
 | |
| +	mov	r8, r12
 | |
| +	andl	r8, 3, COH
 | |
| +	brne	.Lunaligned_dst
 | |
| +
 | |
| +.Laligned_copy:
 | |
| +	sub	len, 32
 | |
| +	brlt	.Lless_than_32
 | |
| +
 | |
| +1:	/* Copy 32 bytes at a time */
 | |
| +	sub	src, 32
 | |
| +	ldm	src, r0-r7
 | |
| +	sub	dst, 32
 | |
| +	sub	len, 32
 | |
| +	stm	dst, r0-r7
 | |
| +	brge	1b
 | |
| +
 | |
| +.Lless_than_32:
 | |
| +	/* Copy 16 more bytes if possible */
 | |
| +	sub	len, -16
 | |
| +	brlt	.Lless_than_16
 | |
| +	sub	src, 16
 | |
| +	ldm	src, r0-r3
 | |
| +	sub	dst, 16
 | |
| +	sub	len, 16
 | |
| +	stm	dst, r0-r3
 | |
| +
 | |
| +.Lless_than_16:
 | |
| +	/* Do the remaining as byte copies */
 | |
| +	sub	len, -16
 | |
| +	breq	2f
 | |
| +1:	ld.ub	r0, --src
 | |
| +	st.b	--dst, r0
 | |
| +	sub	len, 1
 | |
| +	brne	1b
 | |
| +
 | |
| +2:	popm	r0-r7, pc
 | |
| +
 | |
| +.Lunaligned_src:
 | |
| +	/* Make src cacheline-aligned. r8 = (src & 31) */
 | |
| +	sub	len, r8
 | |
| +1:	ld.ub	r0, --src
 | |
| +	st.b	--dst, r0
 | |
| +	sub	r8, 1
 | |
| +	brne	1b
 | |
| +
 | |
| +	/* If dst is word-aligned, we're ready to go */
 | |
| +	pref	src[-4]
 | |
| +	mov	r8, 3
 | |
| +	tst	dst, r8
 | |
| +	breq	.Laligned_copy
 | |
| +
 | |
| +.Lunaligned_dst:
 | |
| +	/* src is aligned, but dst is not. Expect bad performance */
 | |
| +	sub	len, 4
 | |
| +	brlt	2f
 | |
| +1:	ld.w	r0, --src
 | |
| +	st.w	--dst, r0
 | |
| +	sub	len, 4
 | |
| +	brge	1b
 | |
| +
 | |
| +2:	neg	len
 | |
| +	add	pc, pc, len << 2
 | |
| +	.rept	3
 | |
| +	ld.ub	r0, --src
 | |
| +	st.b	--dst, r0
 | |
| +	.endr
 | |
| +
 | |
| +	popm	r0-r7, pc
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/memset.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/memset.S	2006-10-20 10:42:15.000000000 +0200
 | |
| @@ -0,0 +1,60 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway.
 | |
| + */
 | |
| +
 | |
| +#define s r12
 | |
| +#define c r11
 | |
| +#define n r10
 | |
| +
 | |
| +	.text
 | |
| +	.global memset
 | |
| +	.type	memset, @function
 | |
| +
 | |
| +	.global	__memset
 | |
| +	.hidden	__memset
 | |
| +	.type	__memset, @function
 | |
| +
 | |
| +	.align	1
 | |
| +memset:
 | |
| +__memset:
 | |
| +	cp.w	n, 32
 | |
| +	mov	r9, s
 | |
| +	brge	.Llarge_memset
 | |
| +
 | |
| +	sub	n, 1
 | |
| +	retlt	s
 | |
| +1:	st.b	s++, c
 | |
| +	sub	n, 1
 | |
| +	brge	1b
 | |
| +
 | |
| +	retal	r9
 | |
| +
 | |
| +.Llarge_memset:
 | |
| +	mov	r8, r11
 | |
| +	mov	r11, 3
 | |
| +	bfins	r8, r8, 8, 8
 | |
| +	bfins	r8, r8, 16, 16
 | |
| +	tst	s, r11
 | |
| +	breq	2f
 | |
| +
 | |
| +1:	st.b	s++, r8
 | |
| +	sub	n, 1
 | |
| +	tst	s, r11
 | |
| +	brne	1b
 | |
| +
 | |
| +2:	mov	r11, r9
 | |
| +	mov	r9, r8
 | |
| +	sub	n, 8
 | |
| +
 | |
| +3:	st.d	s++, r8
 | |
| +	sub	n, 8
 | |
| +	brge	3b
 | |
| +
 | |
| +	/* If we are done, n == -8 and we'll skip all st.b insns below */
 | |
| +	neg	n
 | |
| +	lsl	n, 1
 | |
| +	add	pc, n
 | |
| +	.rept	7
 | |
| +	st.b	s++, r8
 | |
| +	.endr
 | |
| +	retal	r11
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/strcat.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/strcat.S	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,95 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway
 | |
| + */
 | |
| +
 | |
| +#define s1 r9
 | |
| +#define s2 r11
 | |
| +
 | |
| +	.text
 | |
| +	.global strcat
 | |
| +	.type	strcat, @function
 | |
| +	.align	1
 | |
| +strcat:
 | |
| +	mov	s1, r12
 | |
| +
 | |
| +	/* Make sure s1 is word-aligned */
 | |
| +	mov	r10, s1
 | |
| +	andl	r10, 3, COH
 | |
| +	breq	2f
 | |
| +
 | |
| +	add	pc, pc, r10 << 3
 | |
| +	sub	r0, r0, 0	/* 4-byte nop */
 | |
| +	ld.ub	r8, s1++
 | |
| +	sub	r8, r8, 0
 | |
| +	breq	2f
 | |
| +	ld.ub	r8, s1++
 | |
| +	sub	r8, r8, 0
 | |
| +	breq	3f
 | |
| +	ld.ub	r8, s1++
 | |
| +	sub	r8, r8, 0
 | |
| +	breq	4f
 | |
| +
 | |
| +	/* Find the end of the first string */
 | |
| +5:	ld.w	r8, s1++
 | |
| +	tnbz	r8
 | |
| +	brne	5b
 | |
| +
 | |
| +	sub	s1, 4
 | |
| +
 | |
| +	bfextu	r10, r8, 24, 8
 | |
| +	cp.w	r10, 0
 | |
| +	breq	1f
 | |
| +	sub	s1, -1
 | |
| +	bfextu	r10, r8, 16, 8
 | |
| +	cp.w	r10, 0
 | |
| +	breq	2f
 | |
| +	sub	s1, -1
 | |
| +	bfextu	r10, r8, 8, 8
 | |
| +	cp.w	r10, 0
 | |
| +	breq	3f
 | |
| +	sub	s1, -1
 | |
| +	rjmp	4f
 | |
| +
 | |
| +	/* Now, append s2 */
 | |
| +1:	ld.ub	r8, s2++
 | |
| +	st.b	s1++, r8
 | |
| +	cp.w	r8, 0
 | |
| +	reteq	r12
 | |
| +2:	ld.ub	r8, s2++
 | |
| +	st.b	s1++, r8
 | |
| +	cp.w	r8, 0
 | |
| +	reteq	r12
 | |
| +3:	ld.ub	r8, s2++
 | |
| +	st.b	s1++, r8
 | |
| +	cp.w	r8, 0
 | |
| +	reteq	r12
 | |
| +4:	ld.ub	r8, s2++
 | |
| +	st.b	s1++, r8
 | |
| +	cp.w	r8, 0
 | |
| +	reteq	r12
 | |
| +
 | |
| +	/* Copy one word at a time */
 | |
| +	ld.w	r8, s2++
 | |
| +	tnbz	r8
 | |
| +	breq	2f
 | |
| +1:	st.w	r8, s2++
 | |
| +	ld.w	r8, s2++
 | |
| +	tnbz	r8
 | |
| +	brne	1b
 | |
| +
 | |
| +	/* Copy the remaining bytes */
 | |
| +	bfextu	r10, r8, 24, 8
 | |
| +	st.b	s1++, r10
 | |
| +	cp.w	r10, 0
 | |
| +	reteq	r12
 | |
| +	bfextu	r10, r8, 16, 8
 | |
| +	st.b	s1++, r10
 | |
| +	cp.w	r10, 0
 | |
| +	reteq	r12
 | |
| +	bfextu	r10, r8, 8, 8
 | |
| +	st.b	s1++, r10
 | |
| +	cp.w	r10, 0
 | |
| +	reteq	r12
 | |
| +	st.b	s1++, r8
 | |
| +	retal	r12
 | |
| +	.size	strcat, . - strcat
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/strcmp.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/strcmp.S	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,80 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway.
 | |
| + */
 | |
| +
 | |
| +#define s1 r12
 | |
| +#define s2 r11
 | |
| +#define len r10
 | |
| +
 | |
| +	.text
 | |
| +	.global strcmp
 | |
| +	.type	strcmp, @function
 | |
| +	.align	1
 | |
| +strcmp:
 | |
| +	mov	r8, 3
 | |
| +	tst	s1, r8
 | |
| +	brne	.Lunaligned_s1
 | |
| +	tst	s2, r8
 | |
| +	brne	.Lunaligned_s2
 | |
| +
 | |
| +1:	ld.w	r8, s1++
 | |
| +	ld.w	r9, s2++
 | |
| +	cp.w	r8, r9
 | |
| +	brne	2f
 | |
| +	tnbz	r8
 | |
| +	brne	1b
 | |
| +	retal	0
 | |
| +
 | |
| +2:	bfextu	r12, r8, 24, 8
 | |
| +	bfextu	r11, r9, 24, 8
 | |
| +	sub	r12, r11
 | |
| +	retne	r12
 | |
| +	cp.w	r11, 0
 | |
| +	reteq	0
 | |
| +	bfextu	r12, r8, 16, 8
 | |
| +	bfextu	r11, r9, 16, 8
 | |
| +	sub	r12, r11
 | |
| +	retne	r12
 | |
| +	cp.w	r11, 0
 | |
| +	reteq	0
 | |
| +	bfextu	r12, r8, 8, 8
 | |
| +	bfextu	r11, r9, 8, 8
 | |
| +	sub	r12, r11
 | |
| +	retne	r12
 | |
| +	cp.w	r11, 0
 | |
| +	reteq	0
 | |
| +	bfextu	r12, r8, 0, 8
 | |
| +	bfextu	r11, r9, 0, 8
 | |
| +	sub	r12, r11
 | |
| +	retal	r12
 | |
| +
 | |
| +.Lunaligned_s1:
 | |
| +3:	tst	s1, r8
 | |
| +	breq	4f
 | |
| +	ld.ub	r10, s1++
 | |
| +	ld.ub	r9, s2++
 | |
| +	sub	r10, r9
 | |
| +	retne	r10
 | |
| +	cp.w	r9, 0
 | |
| +	brne	3b
 | |
| +	retal	r10
 | |
| +
 | |
| +4:	tst	s2, r8
 | |
| +	breq	1b
 | |
| +
 | |
| +.Lunaligned_s2:
 | |
| +	/*
 | |
| +	 * s1 and s2 can't both be aligned, and unaligned word loads
 | |
| +	 * can trigger spurious exceptions if we cross a page boundary.
 | |
| +	 * Do it the slow way...
 | |
| +	 */
 | |
| +1:	ld.ub	r8, s1++
 | |
| +	ld.ub	r9, s2++
 | |
| +	sub	r8, r9
 | |
| +	retne	r8
 | |
| +	cp.w	r9, 0
 | |
| +	brne	1b
 | |
| +	retal	0
 | |
| +
 | |
| +	.weak	strcoll
 | |
| +	strcoll	= strcmp
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/strcpy.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/strcpy.S	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,63 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway
 | |
| + *
 | |
| + * To reduce the size, this one might simply call strncpy with len = -1.
 | |
| + */
 | |
| +
 | |
| +#define dst r9
 | |
| +#define src r11
 | |
| +
 | |
| +	.text
 | |
| +	.global strcpy
 | |
| +	.type	strcpy, @function
 | |
| +strcpy:
 | |
| +	mov	dst, r12
 | |
| +
 | |
| +	pref	src[0]
 | |
| +
 | |
| +	/*
 | |
| +	 * Check alignment. If src is aligned but dst isn't, we can't
 | |
| +	 * do much about it...
 | |
| +	 */
 | |
| +	mov	r8, src
 | |
| +	andl	r8, 3 COH
 | |
| +	brne	.Lunaligned_src
 | |
| +
 | |
| +.Laligned_copy:
 | |
| +1:	ld.w	r8, src++
 | |
| +	tnbz	r8
 | |
| +	breq	2f
 | |
| +	st.w	dst++, r8
 | |
| +	rjmp	1b
 | |
| +
 | |
| +2:	/*
 | |
| +	 * Ok, r8 now contains the terminating '\0'. Copy the
 | |
| +	 * remaining bytes individually.
 | |
| +	 */
 | |
| +	bfextu	r10, r8, 24, 8
 | |
| +	st.b	dst++, r10
 | |
| +	cp.w	r10, 0
 | |
| +	reteq	r12
 | |
| +	bfextu	r10, r8, 16, 8
 | |
| +	st.b	dst++, r10
 | |
| +	cp.w	r10, 0
 | |
| +	reteq	r12
 | |
| +	bfextu	r10, r8, 8, 8
 | |
| +	st.b	dst++, r10
 | |
| +	cp.w	r10, 0
 | |
| +	reteq	r12
 | |
| +	st.b	dst++, r8
 | |
| +	retal	r12
 | |
| +
 | |
| +.Lunaligned_src:
 | |
| +	/* Copy bytes until we're aligned */
 | |
| +	rsub	r8, r8, 4
 | |
| +	add	pc, pc, r8 << 3
 | |
| +	nop
 | |
| +	nop
 | |
| +	ld.ub	r10, src++
 | |
| +	st.b	dst++, r10
 | |
| +	cp.w	r10, 0
 | |
| +	reteq	r12
 | |
| +
 | |
| +	rjmp	.Laligned_copy
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/stringtest.c
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/stringtest.c	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,144 @@
 | |
| +
 | |
| +#include <stdio.h>
 | |
| +#include <string.h>
 | |
| +#include <time.h>
 | |
| +#include <sys/mman.h>
 | |
| +
 | |
| +#define BUF_SIZE (8 * 1024)
 | |
| +
 | |
| +static char *buf1;
 | |
| +static char *buf1_ref;
 | |
| +static char *buf2;
 | |
| +
 | |
| +extern void *optimized_memcpy(void *dest, void *src, size_t len);
 | |
| +extern void *optimized_memmove(void *dest, void *src, size_t len);
 | |
| +extern char *optimized_strcpy(char *dest, char *src);
 | |
| +extern char *optimized_strncpy(char *dest, char *src, size_t len);
 | |
| +
 | |
| +void dump_mismatch(char *buf, char *ref, size_t len)
 | |
| +{
 | |
| +	int i, j;
 | |
| +
 | |
| +	for (i = 0; i < len; i += 16) {
 | |
| +		if (memcmp(buf + i, ref + i, 16) == 0)
 | |
| +			continue;
 | |
| +
 | |
| +		printf("%4x buf:", i);
 | |
| +		for (j = i; j < (i + 16); j++)
 | |
| +			printf(" %02x", buf[j]);
 | |
| +		printf("\n     ref:");
 | |
| +		for (j = i; j < (i + 16); j++)
 | |
| +			printf(" %02x", ref[j]);
 | |
| +		printf("\n");
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +static void test_memcpy(int src_offset, int dst_offset, int len)
 | |
| +{
 | |
| +	clock_t start, old, new;
 | |
| +	int i;
 | |
| +
 | |
| +	memset(buf1, 0x55, BUF_SIZE);
 | |
| +	memset(buf1_ref, 0x55, BUF_SIZE);
 | |
| +	memset(buf2, 0xaa, BUF_SIZE);
 | |
| +
 | |
| +	printf("Testing memcpy with offsets %d => %d and len %d...",
 | |
| +	       src_offset, dst_offset, len);
 | |
| +
 | |
| +	start = clock();
 | |
| +	for (i = 0; i < 8192; i++)
 | |
| +		optimized_memcpy(buf1 + dst_offset, buf2 + src_offset, len);
 | |
| +	new = clock() - start;
 | |
| +	start = clock();
 | |
| +	for ( i = 0; i < 8192; i++)
 | |
| +		memcpy(buf1_ref + dst_offset, buf2 + src_offset, len);
 | |
| +	old = clock() - start;
 | |
| +
 | |
| +	if (memcmp(buf1, buf1_ref, BUF_SIZE) == 0)
 | |
| +		printf("OK\n");
 | |
| +	else {
 | |
| +		printf("FAILED\n");
 | |
| +		dump_mismatch(buf1, buf1_ref, BUF_SIZE);
 | |
| +	}
 | |
| +	printf("CPU time used: %d vs. %d\n", new, old);
 | |
| +}
 | |
| +
 | |
| +static void test_memmove(int src_offset, int dst_offset, int len)
 | |
| +{
 | |
| +	clock_t start, old, new;
 | |
| +
 | |
| +	memset(buf1, 0x55, BUF_SIZE);
 | |
| +	memset(buf1_ref, 0x55, BUF_SIZE);
 | |
| +	memset(buf2, 0xaa, BUF_SIZE);
 | |
| +
 | |
| +	printf("Testing memmove with offsets %d => %d and len %d...",
 | |
| +	       src_offset, dst_offset, len);
 | |
| +
 | |
| +	start = clock();
 | |
| +	optimized_memmove(buf1 + dst_offset, buf2 + src_offset, len);
 | |
| +	new = clock() - start;
 | |
| +	start = clock();
 | |
| +	memmove(buf1_ref + dst_offset, buf2 + src_offset, len);
 | |
| +	old = clock() - start;
 | |
| +
 | |
| +	if (memcmp(buf1, buf1_ref, BUF_SIZE) == 0)
 | |
| +		printf("OK\n");
 | |
| +	else {
 | |
| +		printf("FAILED\n");
 | |
| +		dump_mismatch(buf1, buf1_ref, BUF_SIZE);
 | |
| +	}
 | |
| +	printf("CPU time used: %d vs. %d\n", new, old);
 | |
| +}
 | |
| +
 | |
| +int main(int argc, char *argv[])
 | |
| +{
 | |
| +	buf2 = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE,
 | |
| +		    MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
 | |
| +	if (buf2 == MAP_FAILED) {
 | |
| +		perror("Failed to allocate memory for buf2");
 | |
| +		return 1;
 | |
| +	}
 | |
| +	buf1 = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE,
 | |
| +		    MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
 | |
| +	if (buf1 == MAP_FAILED) {
 | |
| +		perror("Failed to allocate memory for buf1");
 | |
| +		return 1;
 | |
| +	}
 | |
| +	buf1_ref = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE,
 | |
| +			MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
 | |
| +	if (buf1_ref == MAP_FAILED) {
 | |
| +		perror("Failed to allocate memory for buf1_ref");
 | |
| +		return 1;
 | |
| +	}
 | |
| +	printf("\n === MEMCPY ===\n\n");
 | |
| +
 | |
| +	test_memcpy(0, 0, BUF_SIZE - 32);
 | |
| +	test_memcpy(0, 0, 1);
 | |
| +	test_memcpy(0, 0, 31);
 | |
| +	test_memcpy(0, 0, 32);
 | |
| +	test_memcpy(0, 0, 127);
 | |
| +	test_memcpy(0, 0, 128);
 | |
| +	test_memcpy(4, 4, BUF_SIZE - 32 - 4);
 | |
| +	test_memcpy(1, 1, BUF_SIZE - 32 - 1);
 | |
| +	test_memcpy(1, 1, 126);
 | |
| +	test_memcpy(0, 3, 128);
 | |
| +	test_memcpy(1, 4, 128);
 | |
| +	test_memcpy(0, 0, 0);
 | |
| +
 | |
| +	printf("\n === MEMMOVE ===\n\n");
 | |
| +
 | |
| +	test_memmove(0, 0, BUF_SIZE - 32);
 | |
| +	test_memmove(0, 0, 1);
 | |
| +	test_memmove(0, 0, 31);
 | |
| +	test_memmove(0, 0, 32);
 | |
| +	test_memmove(0, 0, BUF_SIZE - 33);
 | |
| +	test_memmove(0, 0, 128);
 | |
| +	test_memmove(4, 4, BUF_SIZE - 32 - 4);
 | |
| +	test_memmove(1, 1, BUF_SIZE - 32 - 1);
 | |
| +	test_memmove(1, 1, BUF_SIZE - 130);
 | |
| +	test_memmove(0, 3, BUF_SIZE - 128);
 | |
| +	test_memmove(1, 4, BUF_SIZE - 128);
 | |
| +	test_memmove(0, 0, 0);
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/strlen.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/strlen.S	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,52 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway
 | |
| + */
 | |
| +
 | |
| +#define str r12
 | |
| +
 | |
| +	.text
 | |
| +	.global strlen
 | |
| +	.type	strlen, @function
 | |
| +strlen:
 | |
| +	mov	r11, r12
 | |
| +
 | |
| +	mov	r9, str
 | |
| +	andl	r9, 3, COH
 | |
| +	brne	.Lunaligned_str
 | |
| +
 | |
| +1:	ld.w	r8, str++
 | |
| +	tnbz	r8
 | |
| +	brne	1b
 | |
| +
 | |
| +	sub	r12, r11
 | |
| +	bfextu	r9, r8, 24, 8
 | |
| +	cp.w	r9, 0
 | |
| +	subeq	r12, 4
 | |
| +	reteq	r12
 | |
| +	bfextu	r9, r8, 16, 8
 | |
| +	cp.w	r9, 0
 | |
| +	subeq	r12, 3
 | |
| +	reteq	r12
 | |
| +	bfextu	r9, r8, 8, 8
 | |
| +	cp.w	r9, 0
 | |
| +	subeq	r12, 2
 | |
| +	reteq	r12
 | |
| +	sub	r12, 1
 | |
| +	retal	r12
 | |
| +
 | |
| +.Lunaligned_str:
 | |
| +	add	pc, pc, r9 << 3
 | |
| +	sub	r0, r0, 0	/* 4-byte nop */
 | |
| +	ld.ub	r8, str++
 | |
| +	sub	r8, r8, 0
 | |
| +	breq	1f
 | |
| +	ld.ub	r8, str++
 | |
| +	sub	r8, r8, 0
 | |
| +	breq	1f
 | |
| +	ld.ub	r8, str++
 | |
| +	sub	r8, r8, 0
 | |
| +	brne	1b
 | |
| +
 | |
| +1:	sub	r12, 1
 | |
| +	sub	r12, r11
 | |
| +	retal	r12
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/strncpy.S
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/strncpy.S	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,77 @@
 | |
| +/*
 | |
| + * Copyright (C) 2004 Atmel Norway
 | |
| + */
 | |
| +
 | |
| +#define dst r9
 | |
| +#define src r11
 | |
| +
 | |
| +	.text
 | |
| +	.global strcpy
 | |
| +	.type	strncpy, @function
 | |
| +strncpy:
 | |
| +	mov	dst, r12
 | |
| +
 | |
| +	pref	src[0]
 | |
| +	mov	dst, r12
 | |
| +
 | |
| +	/*
 | |
| +	 * Check alignment. If src is aligned but dst isn't, we can't
 | |
| +	 * do much about it...
 | |
| +	 */
 | |
| +	mov	r8, src
 | |
| +	andl	r8, 3 COH
 | |
| +	brne	.Lunaligned_src
 | |
| +
 | |
| +.Laligned_copy:
 | |
| +	sub	r10, 4
 | |
| +	brlt	3f
 | |
| +1:	ld.w	r8, src++
 | |
| +	tnbz	r8
 | |
| +	breq	2f
 | |
| +	st.w	dst++, r8
 | |
| +	sub	r10, 4
 | |
| +	brne	1b
 | |
| +
 | |
| +3:	sub	r10, -4
 | |
| +	reteq	r12
 | |
| +
 | |
| +	/* This is safe as long as src is word-aligned and r10 > 0 */
 | |
| +	ld.w	r8, src++
 | |
| +
 | |
| +2:	/*
 | |
| +	 * Ok, r8 now contains the terminating '\0'. Copy the
 | |
| +	 * remaining bytes individually.
 | |
| +	 */
 | |
| +	bfextu	r11, r8, 24, 8
 | |
| +	st.b	dst++, r11
 | |
| +	cp.w	r11, 0
 | |
| +	reteq	r12
 | |
| +	sub	r10, 1
 | |
| +	reteq	r12
 | |
| +	bfextu	r11, r8, 16, 8
 | |
| +	st.b	dst++, r11
 | |
| +	cp.w	r11, 0
 | |
| +	reteq	r12
 | |
| +	sub	r10, 1
 | |
| +	reteq	r12
 | |
| +	bfextu	r11, r8, 8, 8
 | |
| +	st.b	dst++, r11
 | |
| +	cp.w	r11, 0
 | |
| +	reteq	r12
 | |
| +	sub	r10, 1
 | |
| +	reteq	r12
 | |
| +	st.b	dst++, r8
 | |
| +	retal	r12
 | |
| +
 | |
| +.Lunaligned_src:
 | |
| +	/* Copy bytes until we're aligned */
 | |
| +	min	r8, r8, r10
 | |
| +	sub	r10, r8
 | |
| +	sub	r8, 1
 | |
| +	retlt	r12
 | |
| +1:	ld.ub	r10, src++
 | |
| +	st.b	dst++, r10
 | |
| +	sub	r8, 1
 | |
| +	brge	1b
 | |
| +
 | |
| +	rjmp	.Laligned_copy
 | |
| Index: uClibc-0.9.28-avr32/libc/string/avr32/test_memcpy.c
 | |
| ===================================================================
 | |
| --- /dev/null	1970-01-01 00:00:00.000000000 +0000
 | |
| +++ uClibc-0.9.28-avr32/libc/string/avr32/test_memcpy.c	2006-10-19 15:05:52.000000000 +0200
 | |
| @@ -0,0 +1,66 @@
 | |
| +
 | |
| +#include <stdio.h>
 | |
| +#include <string.h>
 | |
| +
 | |
| +#define BUF_SIZE 32768
 | |
| +
 | |
| +static char buf1[BUF_SIZE] __attribute__((aligned(32)));
 | |
| +static char buf1_ref[BUF_SIZE] __attribute__((aligned(32)));
 | |
| +static char buf2[BUF_SIZE] __attribute__((aligned(32)));
 | |
| +
 | |
| +extern void *new_memcpy(void *dest, void *src, size_t len);
 | |
| +
 | |
| +void dump_mismatch(char *buf, char *ref, size_t len)
 | |
| +{
 | |
| +	int i, j;
 | |
| +
 | |
| +	for (i = 0; i < len; i += 16) {
 | |
| +		if (memcmp(buf + i, ref + i, 16) == 0)
 | |
| +			continue;
 | |
| +
 | |
| +		printf("% 4x buf:", i);
 | |
| +		for (j = i; j < (i + 16); j++)
 | |
| +			printf(" %02x", buf[j]);
 | |
| +		printf("\n     ref:");
 | |
| +		for (j = i; j < (i + 16); j++)
 | |
| +			printf(" %02x", ref[j]);
 | |
| +		printf("\n");
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +void test(int src_offset, int dst_offset, int len)
 | |
| +{
 | |
| +	memset(buf1, 0x55, sizeof(buf1));
 | |
| +	memset(buf1_ref, 0x55, sizeof(buf1_ref));
 | |
| +	memset(buf2, 0xaa, sizeof(buf2));
 | |
| +
 | |
| +	printf("Testing with offsets %d => %d and len %d...",
 | |
| +	       src_offset, dst_offset, len);
 | |
| +
 | |
| +	new_memcpy(buf1 + dst_offset, buf2 + src_offset, len);
 | |
| +	memcpy(buf1_ref + dst_offset, buf2 + src_offset, len);
 | |
| +
 | |
| +	if (memcmp(buf1, buf1_ref, sizeof(buf1)) == 0)
 | |
| +		printf("OK\n");
 | |
| +	else {
 | |
| +		printf("FAILED\n");
 | |
| +		dump_mismatch(buf1, buf1_ref, sizeof(buf1));
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +int main(int argc, char *argv[])
 | |
| +{
 | |
| +	test(0, 0, BUF_SIZE);
 | |
| +	test(0, 0, 1);
 | |
| +	test(0, 0, 31);
 | |
| +	test(0, 0, 32);
 | |
| +	test(0, 0, 127);
 | |
| +	test(0, 0, 128);
 | |
| +	test(4, 4, BUF_SIZE - 4);
 | |
| +	test(1, 1, BUF_SIZE - 1);
 | |
| +	test(1, 1, 126);
 | |
| +	test(0, 3, 128);
 | |
| +	test(1, 4, 128);
 | |
| +
 | |
| +	return 0;
 | |
| +}
 |