mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
SIMD.
This commit is contained in:
@@ -19,7 +19,8 @@ trap 'rm -f libc.tmp' EXIT
|
||||
-Wl,--initial-memory=16777216 \
|
||||
-Wl,--export=memset \
|
||||
-Wl,--export=memcpy \
|
||||
-Wl,--export=memcmp
|
||||
-Wl,--export=memcmp \
|
||||
-Wl,--export=strncmp
|
||||
|
||||
"$BINARYEN/wasm-ctor-eval" -g -c _initialize libc.wasm -o libc.tmp
|
||||
"$BINARYEN/wasm-opt" -g --strip --strip-producers -c -O3 \
|
||||
|
||||
Binary file not shown.
@@ -6,6 +6,7 @@
|
||||
(export "memset" (func $memset))
|
||||
(export "memcpy" (func $memcpy))
|
||||
(export "memcmp" (func $memcmp))
|
||||
(export "strncmp" (func $strncmp))
|
||||
(func $memset (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
|
||||
(memory.fill
|
||||
(local.get $0)
|
||||
@@ -25,61 +26,54 @@
|
||||
(func $memcmp (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(block $block2
|
||||
(block $block1
|
||||
(block $block
|
||||
(br_if $block
|
||||
(i32.lt_u
|
||||
(local.get $2)
|
||||
(i32.const 8)
|
||||
)
|
||||
(block $block1
|
||||
(block $block
|
||||
(if
|
||||
(i32.ge_u
|
||||
(local.get $2)
|
||||
(i32.const 16)
|
||||
)
|
||||
(br_if $block
|
||||
(i32.and
|
||||
(i32.or
|
||||
(local.get $0)
|
||||
(local.get $1)
|
||||
)
|
||||
(i32.const 7)
|
||||
)
|
||||
)
|
||||
(loop $label
|
||||
(br_if $block1
|
||||
(i64.ne
|
||||
(i64.load
|
||||
(local.get $0)
|
||||
)
|
||||
(i64.load
|
||||
(local.get $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
(local.set $1
|
||||
(i32.add
|
||||
(local.get $1)
|
||||
(i32.const 8)
|
||||
)
|
||||
)
|
||||
(local.set $0
|
||||
(i32.add
|
||||
(local.get $0)
|
||||
(i32.const 8)
|
||||
)
|
||||
)
|
||||
(br_if $label
|
||||
(i32.gt_u
|
||||
(local.tee $2
|
||||
(i32.sub
|
||||
(local.get $2)
|
||||
(i32.const 8)
|
||||
(then
|
||||
(loop $label
|
||||
(br_if $block
|
||||
(v128.any_true
|
||||
(v128.xor
|
||||
(v128.load align=1
|
||||
(local.get $1)
|
||||
)
|
||||
(v128.load align=1
|
||||
(local.get $0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(i32.const 7)
|
||||
)
|
||||
(local.set $1
|
||||
(i32.add
|
||||
(local.get $1)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
(local.set $0
|
||||
(i32.add
|
||||
(local.get $0)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
(br_if $label
|
||||
(i32.gt_u
|
||||
(local.tee $2
|
||||
(i32.sub
|
||||
(local.get $2)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
(i32.const 15)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(br_if $block2
|
||||
(br_if $block1
|
||||
(i32.eqz
|
||||
(local.get $2)
|
||||
)
|
||||
@@ -132,6 +126,179 @@
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
(func $strncmp (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(local $5 i32)
|
||||
(local $6 v128)
|
||||
(block $block2
|
||||
(block $block1
|
||||
(block $block
|
||||
(if
|
||||
(i32.ge_u
|
||||
(local.get $2)
|
||||
(i32.const 16)
|
||||
)
|
||||
(then
|
||||
(loop $label
|
||||
(br_if $block
|
||||
(v128.any_true
|
||||
(v128.xor
|
||||
(v128.load align=1
|
||||
(local.get $1)
|
||||
)
|
||||
(local.tee $6
|
||||
(v128.load align=1
|
||||
(local.get $0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(if
|
||||
(i32.eqz
|
||||
(i8x16.all_true
|
||||
(local.get $6)
|
||||
)
|
||||
)
|
||||
(then
|
||||
(return
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(local.set $1
|
||||
(i32.add
|
||||
(local.get $1)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
(local.set $0
|
||||
(i32.add
|
||||
(local.get $0)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
(br_if $label
|
||||
(i32.gt_u
|
||||
(local.tee $2
|
||||
(i32.sub
|
||||
(local.get $2)
|
||||
(i32.const 16)
|
||||
)
|
||||
)
|
||||
(i32.const 15)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(br_if $block
|
||||
(local.get $2)
|
||||
)
|
||||
(local.set $4
|
||||
(i32.load8_u
|
||||
(local.get $1)
|
||||
)
|
||||
)
|
||||
(local.set $3
|
||||
(i32.load8_u
|
||||
(local.get $0)
|
||||
)
|
||||
)
|
||||
(br $block1)
|
||||
)
|
||||
(br_if $block1
|
||||
(i32.ne
|
||||
(local.tee $3
|
||||
(i32.load8_u
|
||||
(local.get $0)
|
||||
)
|
||||
)
|
||||
(local.tee $4
|
||||
(i32.load8_u
|
||||
(local.get $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(local.set $5
|
||||
(i32.add
|
||||
(local.get $1)
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
(local.set $0
|
||||
(i32.add
|
||||
(local.get $0)
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
(local.set $1
|
||||
(i32.sub
|
||||
(local.get $2)
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
(loop $label1
|
||||
(local.set $2
|
||||
(i32.const 0)
|
||||
)
|
||||
(br_if $block2
|
||||
(i32.eqz
|
||||
(local.get $1)
|
||||
)
|
||||
)
|
||||
(br_if $block2
|
||||
(i32.eqz
|
||||
(local.get $3)
|
||||
)
|
||||
)
|
||||
(local.set $1
|
||||
(i32.sub
|
||||
(local.get $1)
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
(local.set $4
|
||||
(i32.load8_u
|
||||
(local.get $5)
|
||||
)
|
||||
)
|
||||
(local.set $3
|
||||
(i32.load8_u
|
||||
(local.get $0)
|
||||
)
|
||||
)
|
||||
(local.set $5
|
||||
(i32.add
|
||||
(local.get $5)
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
(local.set $0
|
||||
(i32.add
|
||||
(local.get $0)
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
(br_if $label1
|
||||
(i32.eq
|
||||
(local.get $3)
|
||||
(local.get $4)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(local.set $2
|
||||
(i32.sub
|
||||
(local.get $3)
|
||||
(local.get $4)
|
||||
)
|
||||
)
|
||||
)
|
||||
(local.get $2)
|
||||
)
|
||||
;; features section: mutable-globals, nontrapping-float-to-int, simd, bulk-memory, sign-ext, reference-types, multivalue, bulk-memory-opt
|
||||
)
|
||||
|
||||
|
||||
@@ -20,15 +20,17 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
memory []byte
|
||||
module api.Module
|
||||
memset api.Function
|
||||
memcpy api.Function
|
||||
memcmp api.Function
|
||||
memory []byte
|
||||
module api.Module
|
||||
memset api.Function
|
||||
memcpy api.Function
|
||||
memcmp api.Function
|
||||
strncmp api.Function
|
||||
)
|
||||
|
||||
func call(fn api.Function, arg ...uint64) {
|
||||
func call(fn api.Function, arg ...uint64) uint32 {
|
||||
fn.CallWithStack(context.Background(), arg)
|
||||
return uint32(arg[0])
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
@@ -44,6 +46,7 @@ func TestMain(m *testing.M) {
|
||||
memset = mod.ExportedFunction("memset")
|
||||
memcpy = mod.ExportedFunction("memcpy")
|
||||
memcmp = mod.ExportedFunction("memcmp")
|
||||
strncmp = mod.ExportedFunction("strncmp")
|
||||
memory, _ = mod.Memory().Read(0, mod.Memory().Size())
|
||||
|
||||
os.Exit(m.Run())
|
||||
@@ -58,9 +61,9 @@ func Benchmark_memset(b *testing.B) {
|
||||
}
|
||||
b.StopTimer()
|
||||
|
||||
for i, v := range memory[ptr1 : ptr1+size] {
|
||||
if v != 3 {
|
||||
b.Fatal(i, v)
|
||||
for i, got := range memory[ptr1 : ptr1+size] {
|
||||
if got != 3 {
|
||||
b.Fatal(i, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,9 +78,9 @@ func Benchmark_memcpy(b *testing.B) {
|
||||
}
|
||||
b.StopTimer()
|
||||
|
||||
for i, v := range memory[ptr1 : ptr1+size] {
|
||||
if v != 5 {
|
||||
b.Fatal(i, v)
|
||||
for i, got := range memory[ptr1 : ptr1+size] {
|
||||
if got != 5 {
|
||||
b.Fatal(i, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,4 +96,40 @@ func Benchmark_memcmp(b *testing.B) {
|
||||
call(memcmp, ptr1, ptr2, size)
|
||||
}
|
||||
b.StopTimer()
|
||||
|
||||
// ptr1 > ptr2
|
||||
if got := int32(call(memcmp, ptr1, ptr2, size)); got <= 0 {
|
||||
b.Fatal(got)
|
||||
}
|
||||
// ptr1[:size/2] == ptr2[:size/2]
|
||||
if got := int32(call(memcmp, ptr1, ptr2, size/2)); got != 0 {
|
||||
b.Fatal(got)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_strncmp(b *testing.B) {
|
||||
clear(memory)
|
||||
call(memset, ptr1, 7, size)
|
||||
call(memset, ptr2, 7, size)
|
||||
call(memset, ptr2+size/2, 5, size)
|
||||
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
call(strncmp, ptr1, ptr2, size)
|
||||
}
|
||||
b.StopTimer()
|
||||
|
||||
// ptr1 > ptr2
|
||||
if got := int32(call(memcmp, ptr1, ptr2, size)); got <= 0 {
|
||||
b.Fatal(got)
|
||||
}
|
||||
// make ptr1 < ptr2
|
||||
memory[ptr1+size/2] = 0
|
||||
if got := int32(call(memcmp, ptr1, ptr2, size)); got >= 0 {
|
||||
b.Fatal(got)
|
||||
}
|
||||
// ptr1[:size/2] == ptr2[:size/2]
|
||||
if got := int32(call(memcmp, ptr1, ptr2, size/2)); got != 0 {
|
||||
b.Fatal(got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <wasm_simd128.h>
|
||||
|
||||
#if defined(__wasm_bulk_memory__)
|
||||
#ifdef __wasm_bulk_memory__
|
||||
|
||||
void *memset(void *dest, int c, size_t n) {
|
||||
return __builtin_memset(dest, c, n);
|
||||
@@ -18,22 +19,17 @@ void *memmove(void *dest, const void *src, size_t n) {
|
||||
|
||||
#endif
|
||||
|
||||
#define ONES (~(uintmax_t)(0) / UCHAR_MAX)
|
||||
#define HIGHS (ONES * (UCHAR_MAX / 2 + 1))
|
||||
#define HASZERO(x) ((x) - (typeof(x))(ONES) & ~(x) & (typeof(x))(HIGHS))
|
||||
#define UNALIGNED(x) ((uintptr_t)(x) & (sizeof(*x) - 1))
|
||||
#ifdef __wasm_simd128__
|
||||
|
||||
int memcmp(const void *v1, const void *v2, size_t n) {
|
||||
typedef uint64_t __attribute__((__may_alias__)) word;
|
||||
|
||||
const word *w1 = v1;
|
||||
const word *w2 = v2;
|
||||
if (!(UNALIGNED(w1) | UNALIGNED(w2))) {
|
||||
while (n >= sizeof(word) && *w1 == *w2) {
|
||||
n -= sizeof(word);
|
||||
w1++;
|
||||
w2++;
|
||||
const v128_t *w1 = v1;
|
||||
const v128_t *w2 = v2;
|
||||
for (; n >= sizeof(v128_t); n -= sizeof(v128_t)) {
|
||||
if (wasm_v128_any_true(wasm_v128_load(w1) ^ wasm_v128_load(w2))) {
|
||||
break; // *w1 != *w2
|
||||
}
|
||||
w1++;
|
||||
w2++;
|
||||
}
|
||||
|
||||
const unsigned char *u1 = (const void *)w1;
|
||||
@@ -46,6 +42,37 @@ int memcmp(const void *v1, const void *v2, size_t n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int strncmp(const char *c1, const char *c2, size_t n) {
|
||||
const v128_t *w1 = (const void *)c1;
|
||||
const v128_t *w2 = (const void *)c2;
|
||||
for (; n >= sizeof(v128_t); n -= sizeof(v128_t)) {
|
||||
if (wasm_v128_any_true(wasm_v128_load(w1) ^ wasm_v128_load(w2))) {
|
||||
break; // *w1 != *w2
|
||||
}
|
||||
if (!wasm_i8x16_all_true(wasm_v128_load(w1))) {
|
||||
return 0; // *w1 == *w2 and they have a NUL
|
||||
}
|
||||
w1++;
|
||||
w2++;
|
||||
}
|
||||
|
||||
c1 = (const void *)w1;
|
||||
c2 = (const void *)w2;
|
||||
while (n-- && *c1 == *c2) {
|
||||
if (n == 0 || *c1 == 0) return 0;
|
||||
c1++;
|
||||
c2++;
|
||||
}
|
||||
return *(unsigned char *)c1 - *(unsigned char *)c2;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define ONES (~(uintmax_t)(0) / UCHAR_MAX)
|
||||
#define HIGHS (ONES * (UCHAR_MAX / 2 + 1))
|
||||
#define HASZERO(x) ((x) - (typeof(x))(ONES) & ~(x) & (typeof(x))(HIGHS))
|
||||
#define UNALIGNED(x) ((uintptr_t)(x) & (sizeof(*x) - 1))
|
||||
|
||||
int strcmp(const char *c1, const char *c2) {
|
||||
typedef uintptr_t __attribute__((__may_alias__)) word;
|
||||
|
||||
@@ -68,29 +95,6 @@ int strcmp(const char *c1, const char *c2) {
|
||||
return *(unsigned char *)c1 - *(unsigned char *)c2;
|
||||
}
|
||||
|
||||
int strncmp(const char *c1, const char *c2, size_t n) {
|
||||
typedef uintptr_t __attribute__((__may_alias__)) word;
|
||||
|
||||
const word *w1 = (const void *)c1;
|
||||
const word *w2 = (const void *)c2;
|
||||
if (!(UNALIGNED(w1) | UNALIGNED(w2))) {
|
||||
while (n >= sizeof(word) && *w1 == *w2) {
|
||||
if ((n -= sizeof(word)) == 0 || HASZERO(*w1)) return 0;
|
||||
w1++;
|
||||
w2++;
|
||||
}
|
||||
c1 = (const void *)w1;
|
||||
c2 = (const void *)w2;
|
||||
}
|
||||
|
||||
while (n-- && *c1 == *c2) {
|
||||
if (n == 0 || *c1 == 0) return 0;
|
||||
c1++;
|
||||
c2++;
|
||||
}
|
||||
return *(unsigned char *)c1 - *(unsigned char *)c2;
|
||||
}
|
||||
|
||||
#undef UNALIGNED
|
||||
#undef HASZERO
|
||||
#undef HIGHS
|
||||
|
||||
Reference in New Issue
Block a user