string: split strnlen, optimize strlen in assembler (DONE)

This change provides an optimized hand-written strlen function for
SuperH targets. The original plan was to declare the C-based naive
version weak and just let the linker figure out the proper one to use,
but unfortunately static libraries don't work like that; ld
intentionally stops at the first version even if it's weak. Instead,
some #ifdef's are used in the C-based strlen to not compile it when
unneeded.

The optimized strlen uses 4-byte accesses and cmp/str.
This commit is contained in:
Lephenixnoir 2021-05-23 15:57:45 +02:00
parent a48c163e55
commit 6021c536f7
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
5 changed files with 55 additions and 34 deletions

View File

@ -137,7 +137,8 @@ set(SOURCES
src/libc/string/memset.c
src/libc/string/strcmp.c
src/libc/string/strdup.c
src/libc/string/strlen.c)
src/libc/string/strlen.c
src/libc/string/strnlen.c)
if(vhex-generic IN_LIST TARGET_FOLDERS)
# TODO
@ -169,6 +170,7 @@ if(sh-generic IN_LIST TARGET_FOLDERS)
src/libc/setjmp/target/sh-generic/setjmp.S
src/libc/setjmp/target/sh-generic/longjmp.S
src/libc/string/target/sh-generic/memchr.S
src/libc/string/target/sh-generic/strlen.S
src/target/sh-generic/cpucap.c)
endif()

2
STATUS
View File

@ -130,7 +130,7 @@ DONE: Function/symbol/macro is defined, builds, links, and is tested
! 7.21.5.8 strtok: TODO
7.21.6.1 memset: DONE
! 7.21.6.2 strerror: TODO
! 7.21.6.3 strlen: TODO
7.21.6.3 strlen: DONE
Extensions:
- strnlen: TODO
- strchrnul: TODO

View File

@ -1,37 +1,12 @@
#include <string.h>
/*
** The strlen() function calculates the length of the string pointed to by s,
** excluding the terminating null byte ('\0').
**
** TODO: use quad-word access !
*/
size_t strlen(char const *str)
{
size_t i;
#ifndef __SUPPORT_ARCH_SH
if (str == NULL)
return (0);
i = -1;
while (str[++i] != '\0') ;
return (i);
size_t strlen(char const *s)
{
size_t i = 0;
while(s[i]) i++;
return i;
}
/*
** The strnlen() function returns the number of bytes in the string pointed to
** by s, excluding the terminating null byte ('\0'), but at most maxlen.
** In doing this, strnlen() looks only at the first maxlen characters in the
** string pointed to by s and never beyond s+maxlen.
**
** TODO: use quad-word access !
*/
size_t strnlen(char const *str, size_t maxlen)
{
size_t i;
if (str == NULL)
return (0);
i = -1;
while (str[++i] != '\0' && (size_t)i < maxlen) ;
return (i);
}
#endif /*__SUPPORT_ARCH_SH*/

View File

@ -0,0 +1,8 @@
#include <string.h>
size_t strnlen(char const *s, size_t n)
{
size_t i = 0;
while(s[i] && i < n) i++;
return i;
}

View File

@ -0,0 +1,36 @@
.global _strlen
.type _strlen, @function
_strlen:
mov r4, r0
mov #0, r2
/* Check 3 bytes to make sure we don't skip any when aligning */
mov.b @r0+, r1
tst r1, r1
bt .end
mov.b @r0+, r1
tst r1, r1
bt .end
mov.b @r0+, r1
tst r1, r1
bt .end
/* Align to a 4-byte boundary */
or #3, r0
xor #3, r0
/* Read bytes by groups of 4 */
1: mov.l @r0+, r1
cmp/str r1, r2
bf 1b
/* Go back to find out which of the last 4 bytes is the NUL */
add #-4, r0
2: mov.b @r0+, r1
tst r1, r1
bf 2b
.end: add #-1, r0
rts
sub r4, r0