libc: scalar memchr() in RISC-V assembly
Added an optimized memchr() implementation in RISC-V assembly and updated
the relevant manpage.
│ memchr_baseline │ memchr_scalar │
│ sec/op │ sec/op vs base │
Short 636.6µ ± 1% 495.9µ ± 1% -22.10% (p=0.000 n=20)
Mid 279.7µ ± 1% 224.1µ ± 1% -19.87% (p=0.000 n=20)
Long 138.8µ ± 0% 124.9µ ± 0% -10.00% (p=0.000 n=20)
geomean 291.3µ 240.3µ -17.48%
│ memchr_baseline │ memchr_scalar │
│ B/s │ B/s vs base │
Short 187.3Mi ± 1% 240.4Mi ± 1% +28.37% (p=0.000 n=20)
Mid 426.2Mi ± 1% 531.9Mi ± 1% +24.79% (p=0.000 n=20)
Long 859.0Mi ± 0% 954.4Mi ± 0% +11.11% (p=0.000 n=20)
geomean 409.3Mi 496.0Mi +21.19%
MFC after: 1 month
MFC to: stable/15
Approved by: mhorne, markj (mentor)
Reviewed by: fuz
Sponsored by: Google LLC (GSoC 2024)
Differential Revision: https://reviews.freebsd.org/D46023
This commit is contained in:
committed by
Robert Clausecker
parent
63ff982b17
commit
563efdd3bd
@@ -1,2 +1,3 @@
|
||||
MDSRCS+= \
|
||||
memchr.S \
|
||||
strrchr.S
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2024 Strahinja Stanisic <strajabot@FreeBSD.org>
|
||||
*/
|
||||
|
||||
#include <machine/asm.h>
|
||||
|
||||
/*
|
||||
* a0 - const void *b
|
||||
* a1 - int c
|
||||
* a2 - size_t len
|
||||
*/
|
||||
ENTRY(memchr)
|
||||
/*
|
||||
* a0 - const char *ptr
|
||||
* a1 - char cccccccc[8]
|
||||
* a2 - char iter[8]
|
||||
* a3 - uint8_t *end
|
||||
* a4 - uint64_t *end_align
|
||||
* a5 - uint64_t *end_unroll
|
||||
*/
|
||||
|
||||
beqz a2, .Lno_match
|
||||
|
||||
/* c = (uint8_t) c */
|
||||
andi a1, a1, 0xFF
|
||||
|
||||
/*
|
||||
* t0 = 0x0101010101010101
|
||||
* t1 = 0x8080808080808080
|
||||
* t2 = b << 3
|
||||
* cccccccc = (uint8_t)c * t0
|
||||
* end = b + len;
|
||||
* ptr = b & ~0b111
|
||||
*/
|
||||
add a3, a0, a2
|
||||
li t0, 0x01010101
|
||||
sltu t2, a0, a3
|
||||
slli t1, t0, 32
|
||||
neg t2, t2
|
||||
or t0, t0, t1
|
||||
and a3, a3, t2
|
||||
slli t1, t0, 7
|
||||
slli t2, a0, 3
|
||||
and a0, a0, ~0b111
|
||||
mul a1, t0, a1
|
||||
|
||||
ld a2, (a0)
|
||||
|
||||
/*
|
||||
* mask_start = REP8_0x01 ^ (REP8_0x01 << t2)
|
||||
* iter = iter ^ cccccccc
|
||||
* iter = iter | mask_start
|
||||
*/
|
||||
sll t2, t0, t2
|
||||
xor a2, a2, a1
|
||||
xor t2, t2, t0
|
||||
or a2, a2, t2
|
||||
|
||||
/* has_zero(iter)
|
||||
* end_align = (end + 7) & ~0b111;
|
||||
*/
|
||||
addi a4, a3, 7
|
||||
not t2, a2
|
||||
sub a2, a2, t0
|
||||
and t2, t2, t1
|
||||
andi a4, a4, ~0b111
|
||||
and a2, a2, t2
|
||||
|
||||
/* ptr = ptr + 8 */
|
||||
addi a0, a0, 8
|
||||
|
||||
bnez a2, .Lfind_zero
|
||||
|
||||
/* if(ptr == end_align) */
|
||||
beq a0, a4, .Lno_match
|
||||
|
||||
/* end_unroll = end_align & ~0b1111 */
|
||||
andi a5, a4, ~0b1111
|
||||
|
||||
/*
|
||||
* Instead of branching to check if `ptr` is 16-byte aligned:
|
||||
* - Probe the next 8 bytes for `c`
|
||||
* - Align `ptr` down to the nearest 16-byte boundary
|
||||
*
|
||||
* If `ptr` was already 16-byte aligned, those 8 bytes will be
|
||||
* checked again inside the unrolled loop.
|
||||
*
|
||||
* This removes an unpredictable branch and improves performance.
|
||||
*/
|
||||
|
||||
ld a2, (a0)
|
||||
xor a2, a2, a1
|
||||
|
||||
not t2, a2
|
||||
sub a2, a2, t0
|
||||
and t2, t2, t1
|
||||
and a2, a2, t2
|
||||
|
||||
addi a0, a0, 8
|
||||
|
||||
bnez a2, .Lfind_zero
|
||||
|
||||
andi a0, a0, ~0b1111
|
||||
|
||||
/* while(ptr != end_unroll) */
|
||||
beq a0, a5, .Lskip_loop
|
||||
.Lloop:
|
||||
ld a2, (a0)
|
||||
ld t3, 8(a0)
|
||||
|
||||
xor a2, a2, a1
|
||||
xor t3, t3, a1
|
||||
|
||||
not t2, a2
|
||||
not t4, t3
|
||||
sub a2, a2, t0
|
||||
sub t3, t3, t0
|
||||
and t2, t2, t1
|
||||
and t4, t4, t1
|
||||
and a2, a2, t2
|
||||
and t3, t3, t4
|
||||
|
||||
addi a0, a0, 8
|
||||
|
||||
bnez a2, .Lfind_zero
|
||||
|
||||
/* move into iter for find_zero */
|
||||
mv a2, t3
|
||||
|
||||
addi a0, a0, 8
|
||||
|
||||
bnez a2, .Lfind_zero
|
||||
|
||||
bne a0, a5, .Lloop
|
||||
.Lskip_loop:
|
||||
|
||||
/* there might be one 8byte left */
|
||||
beq a0, a4, .Lno_match
|
||||
|
||||
ld a2, (a0)
|
||||
xor a2, a2, a1
|
||||
|
||||
not t2, a2
|
||||
sub a2, a2, t0
|
||||
and t2, t2, t1
|
||||
and a2, a2, t2
|
||||
|
||||
addi a0, a0, 8
|
||||
|
||||
beqz a2, .Lno_match
|
||||
|
||||
.Lfind_zero:
|
||||
/*
|
||||
* ptr = ptr - 8
|
||||
* t1 = 0x0001020304050607
|
||||
* iter = iter & (-iter)
|
||||
* iter = iter >> 7
|
||||
* iter = iter * t1
|
||||
* iter = iter >> 56
|
||||
*/
|
||||
li t1, 0x10203000
|
||||
neg t0, a2
|
||||
slli t1, t1, 4
|
||||
and a2, a2, t0
|
||||
addi t1, t1, 0x405
|
||||
srli a2, a2, 7
|
||||
slli t1, t1, 16
|
||||
addi a0, a0, -8
|
||||
addi t1, t1, 0x607
|
||||
mul a2, a2, t1
|
||||
srli a2, a2, 56
|
||||
|
||||
/* left = end - ptr */
|
||||
sub t0, a3, a0
|
||||
|
||||
/* return iter < left ? ptr + iter : NULL */
|
||||
sltu t1, a2, t0
|
||||
neg t1, t1
|
||||
add a0, a0, a2
|
||||
and a0, a0, t1
|
||||
ret
|
||||
|
||||
.Lno_match:
|
||||
li a0, 0
|
||||
ret
|
||||
END(memchr)
|
||||
Reference in New Issue
Block a user