diff options
author | Ken Steele <ken@tilera.com> | 2013-08-01 15:55:07 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2013-08-01 20:08:51 -0400 |
commit | 5916700c768803546b6fe7d093dcba40d22fcf57 (patch) | |
tree | 07bbeb368b246e82ad23c6af151f4408f7be1cb0 | |
parent | c53c70a90fdce3e7a53a0412abf7cc2b2a645988 (diff) |
tile: optimize strnlen using SIMD instructions
Using strlen as a model, add length checking to create strnlen.
Signed-off-by: Ken Steele <ken@tilera.com>
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
-rw-r--r-- | arch/tile/include/asm/string.h | 2 | ||||
-rw-r--r-- | arch/tile/lib/Makefile | 2 | ||||
-rw-r--r-- | arch/tile/lib/strnlen_32.c | 47 | ||||
-rw-r--r-- | arch/tile/lib/strnlen_64.c | 48 |
4 files changed, 98 insertions, 1 deletions
diff --git a/arch/tile/include/asm/string.h b/arch/tile/include/asm/string.h index 7535cf1a30e4..92b271bd9ebd 100644 --- a/arch/tile/include/asm/string.h +++ b/arch/tile/include/asm/string.h | |||
@@ -21,8 +21,10 @@ | |||
21 | #define __HAVE_ARCH_MEMMOVE | 21 | #define __HAVE_ARCH_MEMMOVE |
22 | #define __HAVE_ARCH_STRCHR | 22 | #define __HAVE_ARCH_STRCHR |
23 | #define __HAVE_ARCH_STRLEN | 23 | #define __HAVE_ARCH_STRLEN |
24 | #define __HAVE_ARCH_STRNLEN | ||
24 | 25 | ||
25 | extern __kernel_size_t strlen(const char *); | 26 | extern __kernel_size_t strlen(const char *); |
27 | extern __kernel_size_t strnlen(const char *, __kernel_size_t); | ||
26 | extern char *strchr(const char *s, int c); | 28 | extern char *strchr(const char *s, int c); |
27 | extern void *memchr(const void *s, int c, size_t n); | 29 | extern void *memchr(const void *s, int c, size_t n); |
28 | extern void *memset(void *, int, __kernel_size_t); | 30 | extern void *memset(void *, int, __kernel_size_t); |
diff --git a/arch/tile/lib/Makefile b/arch/tile/lib/Makefile index 985f59858234..5d844374b2b1 100644 --- a/arch/tile/lib/Makefile +++ b/arch/tile/lib/Makefile | |||
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | lib-y = cacheflush.o checksum.o cpumask.o delay.o uaccess.o \ | 5 | lib-y = cacheflush.o checksum.o cpumask.o delay.o uaccess.o \ |
6 | memmove.o memcpy_$(BITS).o memchr_$(BITS).o memset_$(BITS).o \ | 6 | memmove.o memcpy_$(BITS).o memchr_$(BITS).o memset_$(BITS).o \ |
7 | strchr_$(BITS).o strlen_$(BITS).o | 7 | strchr_$(BITS).o strlen_$(BITS).o strnlen_$(BITS).o |
8 | 8 | ||
9 | ifeq ($(CONFIG_TILEGX),y) | 9 | ifeq ($(CONFIG_TILEGX),y) |
10 | CFLAGS_REMOVE_memcpy_user_64.o = -fno-omit-frame-pointer | 10 | CFLAGS_REMOVE_memcpy_user_64.o = -fno-omit-frame-pointer |
diff --git a/arch/tile/lib/strnlen_32.c b/arch/tile/lib/strnlen_32.c new file mode 100644 index 000000000000..1434141d9e01 --- /dev/null +++ b/arch/tile/lib/strnlen_32.c | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Copyright 2013 Tilera Corporation. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/types.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/module.h> | ||
18 | |||
19 | size_t strnlen(const char *s, size_t count) | ||
20 | { | ||
21 | /* Get an aligned pointer. */ | ||
22 | const uintptr_t s_int = (uintptr_t) s; | ||
23 | const uint32_t *p = (const uint32_t *)(s_int & -4); | ||
24 | size_t bytes_read = sizeof(*p) - (s_int & (sizeof(*p) - 1)); | ||
25 | size_t len; | ||
26 | uint32_t v, bits; | ||
27 | |||
28 | /* Avoid page fault risk by not reading any bytes when count is 0. */ | ||
29 | if (count == 0) | ||
30 | return 0; | ||
31 | |||
32 | /* Read first word, but force bytes before the string to be nonzero. */ | ||
33 | v = *p | ((1 << ((s_int << 3) & 31)) - 1); | ||
34 | |||
35 | while ((bits = __insn_seqb(v, 0)) == 0) { | ||
36 | if (bytes_read >= count) { | ||
37 | /* Read COUNT bytes and didn't find the terminator. */ | ||
38 | return count; | ||
39 | } | ||
40 | v = *++p; | ||
41 | bytes_read += sizeof(v); | ||
42 | } | ||
43 | |||
44 | len = ((const char *) p) + (__insn_ctz(bits) >> 3) - s; | ||
45 | return (len < count ? len : count); | ||
46 | } | ||
47 | EXPORT_SYMBOL(strnlen); | ||
diff --git a/arch/tile/lib/strnlen_64.c b/arch/tile/lib/strnlen_64.c new file mode 100644 index 000000000000..2e8de6a5136f --- /dev/null +++ b/arch/tile/lib/strnlen_64.c | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * Copyright 2013 Tilera Corporation. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/types.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/module.h> | ||
18 | #include "string-endian.h" | ||
19 | |||
20 | size_t strnlen(const char *s, size_t count) | ||
21 | { | ||
22 | /* Get an aligned pointer. */ | ||
23 | const uintptr_t s_int = (uintptr_t) s; | ||
24 | const uint64_t *p = (const uint64_t *)(s_int & -8); | ||
25 | size_t bytes_read = sizeof(*p) - (s_int & (sizeof(*p) - 1)); | ||
26 | size_t len; | ||
27 | uint64_t v, bits; | ||
28 | |||
29 | /* Avoid page fault risk by not reading any bytes when count is 0. */ | ||
30 | if (count == 0) | ||
31 | return 0; | ||
32 | |||
33 | /* Read and MASK the first word. */ | ||
34 | v = *p | MASK(s_int); | ||
35 | |||
36 | while ((bits = __insn_v1cmpeqi(v, 0)) == 0) { | ||
37 | if (bytes_read >= count) { | ||
38 | /* Read COUNT bytes and didn't find the terminator. */ | ||
39 | return count; | ||
40 | } | ||
41 | v = *++p; | ||
42 | bytes_read += sizeof(v); | ||
43 | } | ||
44 | |||
45 | len = ((const char *) p) + (CFZ(bits) >> 3) - s; | ||
46 | return (len < count ? len : count); | ||
47 | } | ||
48 | EXPORT_SYMBOL(strnlen); | ||