Skip to content

Commit

Permalink
Merge patch series "Zbb string optimizations"
Browse files Browse the repository at this point in the history
Heiko Stuebner <heiko@sntech.de> says:

From: Heiko Stuebner <heiko.stuebner@vrull.eu>

This series still tries to allow optimized string functions for specific
extensions. The last approach of using an inline base function to hold
the alternative calls did cause some issues in a number of places

So instead of that we're now just using an alternative j at the beginning
of the generic function to jump to a separate place inside the function
itself.

* b4-shazam-merge:
  RISC-V: add zbb support to string functions
  RISC-V: add infrastructure to allow different str* implementations

Link: https://lore.kernel.org/r/20230113212301.3534711-1-heiko@sntech.de
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
  • Loading branch information
Palmer Dabbelt committed Jan 31, 2023
2 parents 285b6a1 + b6fcdb1 commit 75ab93a
Show file tree
Hide file tree
Showing 12 changed files with 468 additions and 1 deletion.
24 changes: 24 additions & 0 deletions arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,30 @@ config RISCV_ISA_SVPBMT

If you don't know what to do here, say Y.

config TOOLCHAIN_HAS_ZBB
bool
default y
depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64ima_zbb)
depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zbb)
depends on LLD_VERSION >= 150000 || LD_VERSION >= 23900
depends on AS_IS_GNU

config RISCV_ISA_ZBB
bool "Zbb extension support for bit manipulation instructions"
depends on TOOLCHAIN_HAS_ZBB
depends on !XIP_KERNEL && MMU
select RISCV_ALTERNATIVE
default y
help
Adds support to dynamically detect the presence of the ZBB
extension (basic bit manipulation) and enable its usage.

The Zbb extension provides instructions to accelerate a number
of bit-specific operations (count bit population, sign extending,
bitrotation, etc).

If you don't know what to do here, say Y.

config TOOLCHAIN_HAS_ZICBOM
bool
default y
Expand Down
3 changes: 2 additions & 1 deletion arch/riscv/include/asm/errata_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@

#define CPUFEATURE_SVPBMT 0
#define CPUFEATURE_ZICBOM 1
#define CPUFEATURE_NUMBER 2
#define CPUFEATURE_ZBB 2
#define CPUFEATURE_NUMBER 3

#ifdef __ASSEMBLY__

Expand Down
1 change: 1 addition & 0 deletions arch/riscv/include/asm/hwcap.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ enum riscv_isa_ext_id {
RISCV_ISA_EXT_SSTC,
RISCV_ISA_EXT_SVINVAL,
RISCV_ISA_EXT_SVPBMT,
RISCV_ISA_EXT_ZBB,
RISCV_ISA_EXT_ZICBOM,
RISCV_ISA_EXT_ZIHINTPAUSE,
RISCV_ISA_EXT_ID_MAX
Expand Down
10 changes: 10 additions & 0 deletions arch/riscv/include/asm/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ extern asmlinkage void *__memcpy(void *, const void *, size_t);
#define __HAVE_ARCH_MEMMOVE
extern asmlinkage void *memmove(void *, const void *, size_t);
extern asmlinkage void *__memmove(void *, const void *, size_t);

#define __HAVE_ARCH_STRCMP
extern asmlinkage int strcmp(const char *cs, const char *ct);

#define __HAVE_ARCH_STRLEN
extern asmlinkage __kernel_size_t strlen(const char *);

#define __HAVE_ARCH_STRNCMP
extern asmlinkage int strncmp(const char *cs, const char *ct, size_t count);

/* For those files which don't want to check by kasan. */
#if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
#define memcpy(dst, src, len) __memcpy(dst, src, len)
Expand Down
1 change: 1 addition & 0 deletions arch/riscv/kernel/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ arch_initcall(riscv_cpuinfo_init);
* New entries to this struct should follow the ordering rules described above.
*/
static struct riscv_isa_ext_data isa_ext_arr[] = {
__RISCV_ISA_EXT_DATA(zbb, RISCV_ISA_EXT_ZBB),
__RISCV_ISA_EXT_DATA(zicbom, RISCV_ISA_EXT_ZICBOM),
__RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE),
__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
Expand Down
18 changes: 18 additions & 0 deletions arch/riscv/kernel/cpufeature.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ void __init riscv_fill_hwcap(void)
SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC);
SET_ISA_EXT_MAP("svinval", RISCV_ISA_EXT_SVINVAL);
SET_ISA_EXT_MAP("svpbmt", RISCV_ISA_EXT_SVPBMT);
SET_ISA_EXT_MAP("zbb", RISCV_ISA_EXT_ZBB);
SET_ISA_EXT_MAP("zicbom", RISCV_ISA_EXT_ZICBOM);
SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE);
}
Expand Down Expand Up @@ -302,6 +303,20 @@ static bool __init_or_module cpufeature_probe_zicbom(unsigned int stage)
return true;
}

static bool __init_or_module cpufeature_probe_zbb(unsigned int stage)
{
if (!IS_ENABLED(CONFIG_RISCV_ISA_ZBB))
return false;

if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
return false;

if (!riscv_isa_extension_available(NULL, ZBB))
return false;

return true;
}

/*
* Probe presence of individual extensions.
*
Expand All @@ -320,6 +335,9 @@ static u32 __init_or_module cpufeature_probe(unsigned int stage)
if (cpufeature_probe_zicbom(stage))
cpu_req_feature |= BIT(CPUFEATURE_ZICBOM);

if (cpufeature_probe_zbb(stage))
cpu_req_feature |= BIT(CPUFEATURE_ZBB);

return cpu_req_feature;
}

Expand Down
3 changes: 3 additions & 0 deletions arch/riscv/kernel/riscv_ksyms.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
EXPORT_SYMBOL(memset);
EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memmove);
EXPORT_SYMBOL(strcmp);
EXPORT_SYMBOL(strlen);
EXPORT_SYMBOL(strncmp);
EXPORT_SYMBOL(__memset);
EXPORT_SYMBOL(__memcpy);
EXPORT_SYMBOL(__memmove);
3 changes: 3 additions & 0 deletions arch/riscv/lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ lib-y += delay.o
lib-y += memcpy.o
lib-y += memset.o
lib-y += memmove.o
lib-y += strcmp.o
lib-y += strlen.o
lib-y += strncmp.o
lib-$(CONFIG_MMU) += uaccess.o
lib-$(CONFIG_64BIT) += tishift.o

Expand Down
121 changes: 121 additions & 0 deletions arch/riscv/lib/strcmp.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/* SPDX-License-Identifier: GPL-2.0-only */

#include <linux/linkage.h>
#include <asm/asm.h>
#include <asm-generic/export.h>
#include <asm/alternative-macros.h>
#include <asm/errata_list.h>

/* int strcmp(const char *cs, const char *ct) */
SYM_FUNC_START(strcmp)

ALTERNATIVE("nop", "j strcmp_zbb", 0, CPUFEATURE_ZBB, CONFIG_RISCV_ISA_ZBB)

/*
* Returns
* a0 - comparison result, value like strcmp
*
* Parameters
* a0 - string1
* a1 - string2
*
* Clobbers
* t0, t1
*/
1:
lbu t0, 0(a0)
lbu t1, 0(a1)
addi a0, a0, 1
addi a1, a1, 1
bne t0, t1, 2f
bnez t0, 1b
li a0, 0
ret
2:
/*
* strcmp only needs to return (< 0, 0, > 0) values
* not necessarily -1, 0, +1
*/
sub a0, t0, t1
ret

/*
* Variant of strcmp using the ZBB extension if available
*/
#ifdef CONFIG_RISCV_ISA_ZBB
strcmp_zbb:

.option push
.option arch,+zbb

/*
* Returns
* a0 - comparison result, value like strcmp
*
* Parameters
* a0 - string1
* a1 - string2
*
* Clobbers
* t0, t1, t2, t3, t4, t5
*/

or t2, a0, a1
li t4, -1
and t2, t2, SZREG-1
bnez t2, 3f

/* Main loop for aligned string. */
.p2align 3
1:
REG_L t0, 0(a0)
REG_L t1, 0(a1)
orc.b t3, t0
bne t3, t4, 2f
addi a0, a0, SZREG
addi a1, a1, SZREG
beq t0, t1, 1b

/*
* Words don't match, and no null byte in the first
* word. Get bytes in big-endian order and compare.
*/
#ifndef CONFIG_CPU_BIG_ENDIAN
rev8 t0, t0
rev8 t1, t1
#endif

/* Synthesize (t0 >= t1) ? 1 : -1 in a branchless sequence. */
sltu a0, t0, t1
neg a0, a0
ori a0, a0, 1
ret

2:
/*
* Found a null byte.
* If words don't match, fall back to simple loop.
*/
bne t0, t1, 3f

/* Otherwise, strings are equal. */
li a0, 0
ret

/* Simple loop for misaligned strings. */
.p2align 3
3:
lbu t0, 0(a0)
lbu t1, 0(a1)
addi a0, a0, 1
addi a1, a1, 1
bne t0, t1, 4f
bnez t0, 3b

4:
sub a0, t0, t1
ret

.option pop
#endif
SYM_FUNC_END(strcmp)
133 changes: 133 additions & 0 deletions arch/riscv/lib/strlen.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/* SPDX-License-Identifier: GPL-2.0-only */

#include <linux/linkage.h>
#include <asm/asm.h>
#include <asm-generic/export.h>
#include <asm/alternative-macros.h>
#include <asm/errata_list.h>

/* int strlen(const char *s) */
SYM_FUNC_START(strlen)

ALTERNATIVE("nop", "j strlen_zbb", 0, CPUFEATURE_ZBB, CONFIG_RISCV_ISA_ZBB)

/*
* Returns
* a0 - string length
*
* Parameters
* a0 - String to measure
*
* Clobbers:
* t0, t1
*/
mv t1, a0
1:
lbu t0, 0(t1)
beqz t0, 2f
addi t1, t1, 1
j 1b
2:
sub a0, t1, a0
ret

/*
* Variant of strlen using the ZBB extension if available
*/
#ifdef CONFIG_RISCV_ISA_ZBB
strlen_zbb:

#ifdef CONFIG_CPU_BIG_ENDIAN
# define CZ clz
# define SHIFT sll
#else
# define CZ ctz
# define SHIFT srl
#endif

.option push
.option arch,+zbb

/*
* Returns
* a0 - string length
*
* Parameters
* a0 - String to measure
*
* Clobbers
* t0, t1, t2, t3
*/

/* Number of irrelevant bytes in the first word. */
andi t2, a0, SZREG-1

/* Align pointer. */
andi t0, a0, -SZREG

li t3, SZREG
sub t3, t3, t2
slli t2, t2, 3

/* Get the first word. */
REG_L t1, 0(t0)

/*
* Shift away the partial data we loaded to remove the irrelevant bytes
* preceding the string with the effect of adding NUL bytes at the
* end of the string's first word.
*/
SHIFT t1, t1, t2

/* Convert non-NUL into 0xff and NUL into 0x00. */
orc.b t1, t1

/* Convert non-NUL into 0x00 and NUL into 0xff. */
not t1, t1

/*
* Search for the first set bit (corresponding to a NUL byte in the
* original chunk).
*/
CZ t1, t1

/*
* The first chunk is special: compare against the number
* of valid bytes in this chunk.
*/
srli a0, t1, 3
bgtu t3, a0, 3f

/* Prepare for the word comparison loop. */
addi t2, t0, SZREG
li t3, -1

/*
* Our critical loop is 4 instructions and processes data in
* 4 byte or 8 byte chunks.
*/
.p2align 3
1:
REG_L t1, SZREG(t0)
addi t0, t0, SZREG
orc.b t1, t1
beq t1, t3, 1b
2:
not t1, t1
CZ t1, t1

/* Get number of processed words. */
sub t2, t0, t2

/* Add number of characters in the first word. */
add a0, a0, t2
srli t1, t1, 3

/* Add number of characters in the last word. */
add a0, a0, t1
3:
ret

.option pop
#endif
SYM_FUNC_END(strlen)
Loading

0 comments on commit 75ab93a

Please sign in to comment.