fix(verify): remove all go-libraries kernel dependencies from tests

This commit is contained in:
2026-08-07 21:06:14 +02:00
parent f97bea61c5
commit eace06bbd6
7 changed files with 1 additions and 1427 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ and this project adheres to [Conventional Commits](https://www.conventionalcommi
Unreleased changes on the `development` branch.
## [0.29.0] — 2026-08-05
## [0.29.0] — 2026-08-07
RISC-V GOOBJ emission, YMM vector register display, named buffer allocation
in the debugger, two new CLI commands (`diff`, `profile`), go-to-definition in
-55
View File
@@ -4,9 +4,7 @@
package verify
import (
"runtime"
"testing"
"unsafe"
)
func loadABIKernel(t *testing.T) *Kernel {
@@ -80,59 +78,6 @@ func TestABIR14Clobbered(t *testing.T) {
}
}
// TestABILZ4Kernels verifies that the production go-lz4 kernels are ABI-clean:
// they preserve BP and R14 and do not write into the red zone.
func TestABILZ4Kernels(t *testing.T) {
k := loadLZ4Kernel(t)
// wideCopyAVX2 with a real copy.
src := make([]byte, 128)
for i := range src {
src[i] = byte(i)
}
dst := make([]byte, 128)
args := make([]byte, 48)
PutPtr(args, 0, unsafe.Pointer(&dst[0]))
PutUint64(args, 8, 128)
PutUint64(args, 16, 128)
PutPtr(args, 24, unsafe.Pointer(&src[0]))
PutUint64(args, 32, 128)
PutUint64(args, 40, 128)
_, report, err := k.CallFuncChecked("wideCopyAVX2", args)
runtime.KeepAlive(dst)
runtime.KeepAlive(src)
if err != nil {
t.Fatalf("CallFuncChecked(wideCopyAVX2): %v", err)
}
if !report.OK() {
t.Errorf("wideCopyAVX2: %s", report)
}
// decodeBlockAVX2 with a simple block.
decSrc := []byte{0x50, 'H', 'e', 'l', 'l', 'o'}
decDst := make([]byte, 64)
decArgs := make([]byte, 64)
PutPtr(decArgs, 0, unsafe.Pointer(&decSrc[0]))
PutUint64(decArgs, 8, uint64(len(decSrc)))
PutUint64(decArgs, 16, uint64(cap(decSrc)))
PutPtr(decArgs, 24, unsafe.Pointer(&decDst[0]))
PutUint64(decArgs, 32, uint64(len(decDst)))
PutUint64(decArgs, 40, uint64(cap(decDst)))
_, report, err = k.CallFuncChecked("decodeBlockAVX2", decArgs)
runtime.KeepAlive(decSrc)
runtime.KeepAlive(decDst)
if err != nil {
t.Fatalf("CallFuncChecked(decodeBlockAVX2): %v", err)
}
if !report.OK() {
t.Errorf("decodeBlockAVX2: %s", report)
}
}
func TestCallFuncCheckedErrors(t *testing.T) {
k := loadABIKernel(t)
-151
View File
@@ -1,151 +0,0 @@
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
package verify
import (
"bytes"
"math/rand"
"os"
"runtime"
"testing"
"unsafe"
)
const lz4AVX512Path = "../../go-libraries/go-lz4/avx512_amd64.s"
func loadLZ4AVX512Kernel(t *testing.T) *Kernel {
t.Helper()
if _, err := os.Stat(lz4AVX512Path); err != nil {
t.Skipf("sibling kernel not available: %v", err)
}
k, err := Load(lz4AVX512Path)
if err != nil {
t.Fatalf("Load(%s): %v", lz4AVX512Path, err)
}
t.Cleanup(k.Close)
return k
}
func TestAVX512DecodeKnownAnswers(t *testing.T) {
k := loadLZ4AVX512Kernel(t)
tests := []struct {
name string
src []byte
wantN int
wantCode int
}{
{"literals_only", []byte{0x50, 'H', 'e', 'l', 'l', 'o'}, 5, 0},
{"literals_and_match", []byte{0x54, 'A', 'A', 'A', 'A', 'A', 0x05, 0x00, 0x30, 'B', 'B', 'B'}, 16, 0},
{"overlapping", []byte{0x14, 'X', 0x01, 0x00, 0x10, 'Y'}, 10, 0},
{"malformed", []byte{0x50, 'H', 'e'}, 0, 1},
{"zero_offset", []byte{0x14, 'X', 0x00, 0x00}, 0, 2},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dst := make([]byte, 64)
args := make([]byte, 64)
PutPtr(args, 0, unsafe.Pointer(&tt.src[0]))
PutUint64(args, 8, uint64(len(tt.src)))
PutUint64(args, 16, uint64(cap(tt.src)))
PutPtr(args, 24, unsafe.Pointer(&dst[0]))
PutUint64(args, 32, uint64(len(dst)))
PutUint64(args, 40, uint64(cap(dst)))
out, err := k.CallFunc("decodeBlockAVX512", args)
runtime.KeepAlive(tt.src)
runtime.KeepAlive(dst)
if err != nil {
t.Fatalf("CallFunc: %v", err)
}
n := int(GetUint64(out, 48))
code := int(GetUint64(out, 56))
if n != tt.wantN || code != tt.wantCode {
t.Errorf("got (n=%d, code=%d), want (n=%d, code=%d)", n, code, tt.wantN, tt.wantCode)
}
})
}
}
func TestAVX512DifferentialFuzz(t *testing.T) {
k := loadLZ4AVX512Kernel(t)
rng := rand.New(rand.NewSource(77))
for i := 0; i < 3000; i++ {
wantSize := 1 + rng.Intn(4096)
src := genLZ4Block(rng, wantSize)
dstSize := wantSize + 64
goDst := make([]byte, dstSize)
goN, goCode := decodeBlockGo(src, goDst)
jitDst := make([]byte, dstSize)
args := make([]byte, 64)
if len(src) > 0 {
PutPtr(args, 0, unsafe.Pointer(&src[0]))
}
PutUint64(args, 8, uint64(len(src)))
PutUint64(args, 16, uint64(cap(src)))
if dstSize > 0 {
PutPtr(args, 24, unsafe.Pointer(&jitDst[0]))
}
PutUint64(args, 32, uint64(dstSize))
PutUint64(args, 40, uint64(cap(jitDst)))
out, err := k.CallFunc("decodeBlockAVX512", args)
runtime.KeepAlive(src)
runtime.KeepAlive(jitDst)
if err != nil {
t.Fatalf("iter %d: %v", i, err)
}
jitN := int(GetUint64(out, 48))
jitCode := int(GetUint64(out, 56))
if jitCode != goCode {
t.Fatalf("iter %d: code mismatch: JIT=%d Go=%d", i, jitCode, goCode)
}
if jitCode != 0 {
continue
}
if jitN != goN {
t.Fatalf("iter %d: n mismatch: JIT=%d Go=%d", i, jitN, goN)
}
if !bytes.Equal(jitDst[:jitN], goDst[:goN]) {
t.Fatalf("iter %d: output mismatch (n=%d)", i, jitN)
}
}
}
func TestAVX512WideCopy(t *testing.T) {
k := loadLZ4AVX512Kernel(t)
sizes := []int{0, 1, 31, 32, 63, 64, 65, 127, 128, 256, 1024}
for _, n := range sizes {
src := make([]byte, n)
for i := range src {
src[i] = byte(i*11 + 3)
}
dst := make([]byte, n)
args := make([]byte, 48)
if n > 0 {
PutPtr(args, 0, unsafe.Pointer(&dst[0]))
PutPtr(args, 24, unsafe.Pointer(&src[0]))
}
PutUint64(args, 8, uint64(n))
PutUint64(args, 16, uint64(n))
PutUint64(args, 32, uint64(n))
PutUint64(args, 40, uint64(n))
_, err := k.CallFunc("wideCopyAVX512", args)
runtime.KeepAlive(dst)
runtime.KeepAlive(src)
if err != nil {
t.Fatalf("wideCopyAVX512(n=%d): %v", n, err)
}
if !bytes.Equal(dst, src) {
t.Errorf("wideCopyAVX512(n=%d): mismatch", n)
}
}
}
-65
View File
@@ -4,9 +4,7 @@
package verify
import (
"runtime"
"testing"
"unsafe"
)
func TestBlocks(t *testing.T) {
@@ -25,66 +23,3 @@ func TestBlocks(t *testing.T) {
}
t.Logf("sum blocks: %v", blocks)
}
func TestBlockCount(t *testing.T) {
k := loadLZ4Kernel(t)
n, err := k.BlockCount("decodeBlockAVX2")
if err != nil {
t.Fatalf("BlockCount: %v", err)
}
// The decoder has many labels (dec_loop, dec_malformed, etc.).
if n < 10 {
t.Errorf("decodeBlockAVX2: expected at least 10 blocks, got %d", n)
}
t.Logf("decodeBlockAVX2: %d basic blocks", n)
}
func TestProfilePaths(t *testing.T) {
k := loadLZ4Kernel(t)
// Build a corpus of varied LZ4 blocks.
var argSets [][]byte
var liveDsts [][]byte // keep dst buffers alive across ProfilePaths
blocks := []struct {
src []byte
dstSize int
}{
{[]byte{0x00}, 16}, // empty
{[]byte{0x50, 'H', 'e', 'l', 'l', 'o'}, 16}, // literals only
{[]byte{0x54, 'A', 'A', 'A', 'A', 'A', 5, 0, 0x30, 'B', 'B', 'B'}, 32}, // match
{[]byte{0x14, 'X', 1, 0, 0x10, 'Y'}, 16}, // overlapping
{[]byte{0x50, 'H'}, 16}, // malformed
{[]byte{0x14, 'X', 0, 0}, 16}, // zero offset
}
for _, b := range blocks {
args := make([]byte, 64)
if len(b.src) > 0 {
PutPtr(args, 0, unsafe.Pointer(&b.src[0]))
}
PutUint64(args, 8, uint64(len(b.src)))
PutUint64(args, 16, uint64(cap(b.src)))
dst := make([]byte, b.dstSize)
liveDsts = append(liveDsts, dst)
if len(dst) > 0 {
PutPtr(args, 24, unsafe.Pointer(&dst[0]))
}
PutUint64(args, 32, uint64(len(dst)))
PutUint64(args, 40, uint64(cap(dst)))
argSets = append(argSets, args)
}
// Result offsets: n+48 and code+56.
paths, err := k.ProfilePaths("decodeBlockAVX2", argSets, []int{48, 56})
runtime.KeepAlive(blocks)
runtime.KeepAlive(liveDsts)
if err != nil {
t.Fatalf("ProfilePaths: %v", err)
}
// We expect at least 3 distinct paths: success (various n), malformed, zero offset.
if len(paths) < 3 {
t.Errorf("expected at least 3 distinct paths, got %d", len(paths))
}
t.Logf("decodeBlockAVX2: %d distinct output paths from %d inputs", len(paths), len(argSets))
}
-298
View File
@@ -1,298 +0,0 @@
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
package verify
import (
"bytes"
"math/rand"
"runtime"
"testing"
"unsafe"
)
// decodeBlockGo is a minimal portable LZ4 block decoder used as the
// differential-testing oracle. It mirrors the contract of
// go-lz4's decodeBlockGo: (bytesWritten, code) where code is
// 0 = ok, 1 = malformed, 2 = zero offset.
func decodeBlockGo(src, dst []byte) (int, int) {
if len(src) == 0 {
return 0, 1
}
si, di := 0, 0
for {
if si >= len(src) {
return 0, 1 // truncated: no token
}
token := int(src[si])
si++
// Literals.
lLen := token >> 4
if lLen == 15 {
for {
if si >= len(src) {
return 0, 1
}
b := int(src[si])
si++
lLen += b
if b != 255 {
break
}
}
}
if si+lLen > len(src) {
return 0, 1 // truncated literals
}
if di+lLen > len(dst) {
return 0, 1 // destination overflow
}
copy(dst[di:di+lLen], src[si:si+lLen])
di += lLen
si += lLen
// End of block.
if si >= len(src) {
return di, 0
}
// Match offset.
if si+2 > len(src) {
return 0, 1
}
offset := int(src[si]) | int(src[si+1])<<8
si += 2
if offset == 0 {
return 0, 2
}
// Match length.
mLen := token & 15
if mLen == 15 {
for {
if si >= len(src) {
return 0, 1
}
b := int(src[si])
si++
mLen += b
if b != 255 {
break
}
}
}
mLen += 4
// Copy match (overlapping-safe).
if di-offset < 0 {
return 0, 1 // offset reaches before dst start
}
if di+mLen > len(dst) {
return 0, 1 // destination overflow
}
for i := 0; i < mLen; i++ {
dst[di+i] = dst[di-offset+i]
}
di += mLen
}
}
// genLZ4Block generates a random valid LZ4 block that decompresses into
// approximately wantSize bytes. The block is always well-formed (ends with
// a literals-only sequence).
func genLZ4Block(rng *rand.Rand, wantSize int) []byte {
var block []byte
produced := 0
for produced < wantSize {
remaining := wantSize - produced
// Decide: emit a literals+match sequence or the final literals.
if remaining <= 8 || rng.Intn(4) == 0 {
// Final literals-only sequence.
lLen := remaining
if lLen > 60 {
lLen = 1 + rng.Intn(60)
}
block = appendToken(block, lLen, 0)
for i := 0; i < lLen; i++ {
block = append(block, byte(rng.Intn(256)))
}
produced += lLen
break
}
// Literals + match.
lLen := rng.Intn(min(16, remaining))
if produced+lLen == 0 {
lLen = 1 // must have at least 1 literal before the first match
}
mLenRaw := rng.Intn(12) // match length = mLenRaw + 4
mLen := mLenRaw + 4
if produced+mLen > remaining {
mLen = remaining - produced
if mLen < 4 {
// Not enough room for a match; emit final literals.
lLen = remaining
block = appendToken(block, lLen, 0)
for i := 0; i < lLen; i++ {
block = append(block, byte(rng.Intn(256)))
}
break
}
mLenRaw = mLen - 4
}
block = appendToken(block, lLen, mLenRaw)
for i := 0; i < lLen; i++ {
block = append(block, byte(rng.Intn(256)))
}
produced += lLen
// Offset: must be <= produced (can't reference before start).
maxOff := produced
if maxOff > 65535 {
maxOff = 65535
}
offset := 1 + rng.Intn(maxOff)
block = append(block, byte(offset), byte(offset>>8))
produced += mLen
}
return block
}
// appendToken appends a token (and extension bytes if needed) for the given
// literal and match lengths.
func appendToken(block []byte, lLen, mLenRaw int) []byte {
lit4 := lLen
if lit4 > 15 {
lit4 = 15
}
ml4 := mLenRaw
if ml4 > 15 {
ml4 = 15
}
block = append(block, byte(lit4<<4|ml4))
// Literal extension bytes.
rem := lLen - 15
for rem >= 255 {
block = append(block, 255)
rem -= 255
}
if lLen >= 15 {
block = append(block, byte(rem))
}
// Match extension bytes.
rem = mLenRaw - 15
for rem >= 255 {
block = append(block, 255)
rem -= 255
}
if mLenRaw >= 15 {
block = append(block, byte(rem))
}
return block
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
// TestDifferentialLZ4Fuzz drives the JIT-assembled decodeBlockAVX2 with
// random valid LZ4 blocks and compares the output bit-for-bit against the
// portable Go reference.
func TestDifferentialLZ4Fuzz(t *testing.T) {
k := loadLZ4Kernel(t)
const iterations = 5000
rng := rand.New(rand.NewSource(42))
for i := 0; i < iterations; i++ {
wantSize := 1 + rng.Intn(4096)
src := genLZ4Block(rng, wantSize)
dstSize := wantSize + 64 // generous destination
// Go reference.
goDst := make([]byte, dstSize)
goN, goCode := decodeBlockGo(src, goDst)
// JIT kernel.
jitDst := make([]byte, dstSize)
jitN, jitCode := callDecodeBlockAVX2(t, k, src, jitDst)
if jitCode != goCode {
t.Fatalf("iter %d: code mismatch: JIT=%d, Go=%d (src len=%d)",
i, jitCode, goCode, len(src))
}
if jitCode != 0 {
continue // both agree it's malformed/zero-offset
}
if jitN != goN {
t.Fatalf("iter %d: n mismatch: JIT=%d, Go=%d (src len=%d)",
i, jitN, goN, len(src))
}
if !bytes.Equal(jitDst[:jitN], goDst[:goN]) {
t.Fatalf("iter %d: output mismatch (n=%d, src len=%d)", i, jitN, len(src))
}
}
}
// TestDifferentialLZ4Hostile drives the kernel with random garbage to check
// that error codes agree with the Go reference (no crashes, same classification).
func TestDifferentialLZ4Hostile(t *testing.T) {
k := loadLZ4Kernel(t)
const iterations = 2000
rng := rand.New(rand.NewSource(99))
for i := 0; i < iterations; i++ {
srcLen := rng.Intn(128)
src := make([]byte, srcLen)
rng.Read(src)
dstSize := rng.Intn(512)
dst := make([]byte, dstSize)
// Go reference.
goDst := make([]byte, dstSize)
copy(goDst, dst)
_, goCode := decodeBlockGo(src, goDst)
// JIT kernel.
jitDst := make([]byte, dstSize)
copy(jitDst, dst)
_, jitCode := callDecodeBlockAVX2(t, k, src, jitDst)
if jitCode != goCode {
t.Fatalf("iter %d: hostile code mismatch: JIT=%d, Go=%d (srcLen=%d, dstSize=%d)",
i, jitCode, goCode, srcLen, dstSize)
}
}
}
// callDecodeBlockAVX2Raw is like callDecodeBlockAVX2 but accepts explicit
// dst size (for hostile tests where dst may be smaller than the output).
func callDecodeBlockAVX2Raw(t *testing.T, k *Kernel, src, dst []byte) (int, int) {
t.Helper()
args := make([]byte, 64)
if len(src) > 0 {
PutPtr(args, 0, unsafe.Pointer(&src[0]))
}
PutUint64(args, 8, uint64(len(src)))
PutUint64(args, 16, uint64(cap(src)))
if len(dst) > 0 {
PutPtr(args, 24, unsafe.Pointer(&dst[0]))
}
PutUint64(args, 32, uint64(len(dst)))
PutUint64(args, 40, uint64(cap(dst)))
out, err := k.CallFunc("decodeBlockAVX2", args)
runtime.KeepAlive(src)
runtime.KeepAlive(dst)
if err != nil {
t.Fatalf("CallFunc(decodeBlockAVX2): %v", err)
}
return int(GetUint64(out, 48)), int(GetUint64(out, 56))
}
-692
View File
@@ -1,692 +0,0 @@
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
package verify
import (
"bytes"
"math/rand"
"os"
"runtime"
"testing"
"unsafe"
)
const flacKernelPath = "../../go-libraries/go-flac/avx2_amd64.s"
func loadFLACKernel(t *testing.T) *Kernel {
t.Helper()
if _, err := os.Stat(flacKernelPath); err != nil {
t.Skipf("sibling kernel not available: %v", err)
}
k, err := Load(flacKernelPath)
if err != nil {
t.Fatalf("Load(%s): %v", flacKernelPath, err)
}
t.Cleanup(k.Close)
return k
}
// --- Portable Go references (from go-flac/simd.go) ---
func decodeMono16Go(src []byte, dst []int32) {
for i := 0; i < len(dst); i++ {
dst[i] = int32(int16(uint16(src[2*i]) | uint16(src[2*i+1])<<8))
}
}
func pack16Go(dst []byte, src []int32) {
for i, v := range src {
dst[2*i] = byte(v)
dst[2*i+1] = byte(v >> 8)
}
}
func decorrelateLeftSideGo(left, right, out []int32) {
for i := range left {
l := left[i]
out[2*i] = l
out[2*i+1] = l - right[i]
}
}
func decorrelateSideRightGo(left, right, out []int32) {
for i := range left {
side := left[i]
rch := right[i]
out[2*i] = rch + side
out[2*i+1] = rch
}
}
func decorrelateMidSideGo(left, right, out []int32) {
for i := range left {
mid := left[i]
side := right[i]
mid2 := mid<<1 | (side & 1)
out[2*i] = (mid2 + side) >> 1
out[2*i+1] = (mid2 - side) >> 1
}
}
func decorrelateInterleaveGo(left, right, out []int32) {
for i := range left {
out[2*i] = left[i]
out[2*i+1] = right[i]
}
}
func analyzeO1RangeGo(swin []int32, dstP []uint32, hist *[32]uint16) (partSum uint64, overflow bool) {
swin = swin[:len(dstP)+1]
for j := 0; j+1 < len(swin); j++ {
r := swin[j+1] - swin[j]
if r == -2147483648 { // math.MinInt32
overflow = true
}
f := uint32(r<<1) ^ uint32(r>>31)
dstP[j] = f
partSum += uint64(f)
bl := 0
for v := f; v > 0; v >>= 1 {
bl++
}
if bl > 31 {
bl = 31
}
hist[bl]++
}
return
}
func analyzeO2RangeGo(swin []int32, dstP []uint32, hist *[32]uint16) (partSum uint64, overflow bool) {
swin = swin[:len(dstP)+2]
for j := 0; j+2 < len(swin); j++ {
r := swin[j+2] - 2*swin[j+1] + swin[j]
if r == -2147483648 {
overflow = true
}
f := uint32(r<<1) ^ uint32(r>>31)
dstP[j] = f
partSum += uint64(f)
bl := 0
for v := f; v > 0; v >>= 1 {
bl++
}
if bl > 31 {
bl = 31
}
hist[bl]++
}
return
}
func analyzeResRangeGo(swin []int32, dstP []uint32, hist *[32]uint16) (partSum uint64, overflow bool) {
for j := 0; j < len(swin); j++ {
r := swin[j]
if r == -2147483648 {
overflow = true
}
f := uint32(r<<1) ^ uint32(r>>31)
dstP[j] = f
partSum += uint64(f)
bl := 0
for v := f; v > 0; v >>= 1 {
bl++
}
if bl > 31 {
bl = 31
}
hist[bl]++
}
return
}
func decodeMono24Go(src []byte, dst []int32) {
for i := 0; i < len(dst); i++ {
off := 3 * i
u := uint32(src[off]) | uint32(src[off+1])<<8 | uint32(src[off+2])<<16
dst[i] = int32(u<<8) >> 8
}
}
func decodeStereo16Go(src []byte, left, right []int32) {
for i := 0; i < len(left); i++ {
left[i] = int32(int16(uint16(src[4*i]) | uint16(src[4*i+1])<<8))
right[i] = int32(int16(uint16(src[4*i+2]) | uint16(src[4*i+3])<<8))
}
}
// --- Differential tests ---
// Buffers whose addresses are passed to JIT code via unsafe.Pointer are
// package-level globals: their addresses are stable and the GC never moves
// or collects them, unlike per-iteration make() buffers (which caused flaky
// stale reads, especially under -race). Sizes cover the maximum n from the
// rng.Intn(N) call in each test, rounded up.
var (
mono16Src [512]byte // TestFLACDecodeMono16: max n = 255 → 2*n = 510
mono16JitDst [256]int32 // max n = 255
pack16Src [256]int32 // TestFLACPack16: max n = 255
pack16JitDst [512]byte // max 2*n = 510
decorLeft [128]int32 // TestFLACDecorrelate: max n = 127
decorRight [128]int32 // max n = 127
decorJitOut [256]int32 // max 2*n = 254
mono24Src [768]byte // TestFLACDecodeMono24: max n = 255 → 3*n = 765
mono24JitDst [256]int32 // max n = 255
stereo16Src [1024]byte // TestFLACDecodeStereo16: max n = 255 → 4*n = 1020
stereo16JitLeft [256]int32 // max n = 255
stereo16JitRight [256]int32 // max n = 255
fastStereoLeft [256]int32 // TestFLACFastStereoSums: max n = 255
fastStereoRight [256]int32 // max n = 255
)
func TestFLACDecodeMono16(t *testing.T) {
k := loadFLACKernel(t)
rng := rand.New(rand.NewSource(7))
src := mono16Src[:0]
jitDst := mono16JitDst[:0]
for iter := 0; iter < 500; iter++ {
n := rng.Intn(256)
src = src[:2*n]
rng.Read(src)
goDst := make([]int32, n)
decodeMono16Go(src, goDst)
jitDst = jitDst[:n]
for i := range jitDst {
jitDst[i] = 0
}
args := make([]byte, 48)
if len(src) > 0 {
PutPtr(args, 0, unsafe.Pointer(&src[0]))
}
PutUint64(args, 8, uint64(len(src)))
PutUint64(args, 16, uint64(cap(src)))
if n > 0 {
PutPtr(args, 24, unsafe.Pointer(&jitDst[0]))
}
PutUint64(args, 32, uint64(n))
PutUint64(args, 40, uint64(cap(jitDst)))
_, err := k.CallFunc("decodeMono16AVX2", args)
runtime.KeepAlive(src)
runtime.KeepAlive(jitDst)
if err != nil {
t.Fatalf("iter %d: %v", iter, err)
}
for i := range goDst {
if jitDst[i] != goDst[i] {
t.Fatalf("iter %d: mismatch at [%d]: JIT=%d Go=%d", iter, i, jitDst[i], goDst[i])
}
}
}
}
func TestFLACPack16(t *testing.T) {
k := loadFLACKernel(t)
rng := rand.New(rand.NewSource(13))
src := pack16Src[:0]
jitDst := pack16JitDst[:0]
for iter := 0; iter < 500; iter++ {
n := rng.Intn(256)
src = src[:n]
for i := range src {
src[i] = int32(rng.Intn(65536) - 32768)
}
goDst := make([]byte, 2*n)
pack16Go(goDst, src)
jitDst = jitDst[:2*n]
for i := range jitDst {
jitDst[i] = 0
}
args := make([]byte, 48)
if len(jitDst) > 0 {
PutPtr(args, 0, unsafe.Pointer(&jitDst[0]))
}
PutUint64(args, 8, uint64(len(jitDst)))
PutUint64(args, 16, uint64(cap(jitDst)))
if n > 0 {
PutPtr(args, 24, unsafe.Pointer(&src[0]))
}
PutUint64(args, 32, uint64(n))
PutUint64(args, 40, uint64(cap(src)))
_, err := k.CallFunc("pack16AVX2", args)
runtime.KeepAlive(jitDst)
runtime.KeepAlive(src)
if err != nil {
t.Fatalf("iter %d: %v", iter, err)
}
if !bytes.Equal(jitDst, goDst) {
t.Fatalf("iter %d: output mismatch (n=%d)", iter, n)
}
}
}
func TestFLACDecorrelate(t *testing.T) {
k := loadFLACKernel(t)
rng := rand.New(rand.NewSource(21))
kernels := []struct {
name string
ref func(left, right, out []int32)
}{
{"decorrelateLeftSideAVX2", decorrelateLeftSideGo},
{"decorrelateSideRightAVX2", decorrelateSideRightGo},
{"decorrelateMidSideAVX2", decorrelateMidSideGo},
{"decorrelateInterleaveAVX2", decorrelateInterleaveGo},
}
for _, kk := range kernels {
t.Run(kk.name, func(t *testing.T) {
left := decorLeft[:0]
right := decorRight[:0]
jitOut := decorJitOut[:0]
for iter := 0; iter < 200; iter++ {
n := rng.Intn(128)
left = left[:n]
right = right[:n]
for i := range left {
left[i] = int32(rng.Intn(1<<24) - 1<<23)
right[i] = int32(rng.Intn(1<<24) - 1<<23)
}
goOut := make([]int32, 2*n)
kk.ref(left, right, goOut)
jitOut = jitOut[:2*n]
for i := range jitOut {
jitOut[i] = 0
}
args := make([]byte, 72)
if n > 0 {
PutPtr(args, 0, unsafe.Pointer(&left[0]))
PutPtr(args, 24, unsafe.Pointer(&right[0]))
PutPtr(args, 48, unsafe.Pointer(&jitOut[0]))
}
PutUint64(args, 8, uint64(n))
PutUint64(args, 16, uint64(cap(left)))
PutUint64(args, 32, uint64(n))
PutUint64(args, 40, uint64(cap(right)))
PutUint64(args, 56, uint64(2*n))
PutUint64(args, 64, uint64(cap(jitOut)))
_, err := k.CallFunc(kk.name, args)
runtime.KeepAlive(left)
runtime.KeepAlive(right)
runtime.KeepAlive(jitOut)
if err != nil {
t.Fatalf("iter %d: %v", iter, err)
}
for i := range goOut {
if jitOut[i] != goOut[i] {
t.Fatalf("iter %d: mismatch at [%d]: JIT=%d Go=%d", iter, i, jitOut[i], goOut[i])
}
}
}
})
}
}
func TestFLACAnalyzeO1Range(t *testing.T) {
k := loadFLACKernel(t)
rng := rand.New(rand.NewSource(33))
swin := analyzeSwin[:0]
jitDstP := analyzeJitDstP[:0]
for iter := 0; iter < 300; iter++ {
n := 1 + rng.Intn(128) // partition size
swin = swin[:n+1]
for i := range swin {
swin[i] = int32(rng.Intn(1<<20) - 1<<19)
}
goDstP := make([]uint32, n)
var goHist [32]uint16
goSum, goOvf := analyzeO1RangeGo(swin, goDstP, &goHist)
jitDstP = jitDstP[:n]
for i := range jitDstP {
jitDstP[i] = 0
}
jitHist := &analyzeJitHist
*jitHist = [32]uint16{} // reset for this iteration
args := make([]byte, 72) // 65 rounded up
PutPtr(args, 0, unsafe.Pointer(&swin[0]))
PutUint64(args, 8, uint64(len(swin)))
PutUint64(args, 16, uint64(cap(swin)))
PutPtr(args, 24, unsafe.Pointer(&jitDstP[0]))
PutUint64(args, 32, uint64(n))
PutUint64(args, 40, uint64(cap(jitDstP)))
PutPtr(args, 48, unsafe.Pointer(&jitHist[0]))
out, err := k.CallFunc("analyzeO1RangeAVX2", args)
runtime.KeepAlive(swin)
runtime.KeepAlive(jitDstP)
runtime.KeepAlive(jitHist)
if err != nil {
t.Fatalf("iter %d: %v", iter, err)
}
jitSum := GetUint64(out, 56)
jitOvf := out[64] != 0
if jitSum != goSum {
t.Fatalf("iter %d: partSum mismatch: JIT=%d Go=%d", iter, jitSum, goSum)
}
if jitOvf != goOvf {
t.Fatalf("iter %d: overflow mismatch: JIT=%v Go=%v", iter, jitOvf, goOvf)
}
for i := range goDstP {
if jitDstP[i] != goDstP[i] {
t.Fatalf("iter %d: dstP[%d] mismatch: JIT=%d Go=%d", iter, i, jitDstP[i], goDstP[i])
}
}
if *jitHist != goHist {
t.Fatalf("iter %d: hist mismatch: JIT=%v Go=%v", iter, *jitHist, goHist)
}
}
}
func TestFLACFastStereoSums(t *testing.T) {
k := loadFLACKernel(t)
rng := rand.New(rand.NewSource(44))
left := fastStereoLeft[:0]
right := fastStereoRight[:0]
for iter := 0; iter < 300; iter++ {
n := 1 + rng.Intn(256)
left = left[:n]
right = right[:n]
for i := range left {
left[i] = int32(rng.Intn(1<<24) - 1<<23)
right[i] = int32(rng.Intn(1<<24) - 1<<23)
}
// Go reference: compute the four sums.
var goSums [4]uint64
for i := 0; i < n; i++ {
l := left[i]
r := right[i]
side := l - r
mid := (l + r) >> 1
goSums[0] += foldAbs(l) + foldAbs(r)
goSums[1] += foldAbs(l) + foldAbs(side)
goSums[2] += foldAbs(side) + foldAbs(r)
goSums[3] += foldAbs(mid) + foldAbs(side)
}
jitSums := &analyzeJitSums
*jitSums = [4]uint64{} // reset for this iteration
args := make([]byte, 56)
PutPtr(args, 0, unsafe.Pointer(&left[0]))
PutUint64(args, 8, uint64(n))
PutUint64(args, 16, uint64(cap(left)))
PutPtr(args, 24, unsafe.Pointer(&right[0]))
PutUint64(args, 32, uint64(n))
PutUint64(args, 40, uint64(cap(right)))
PutPtr(args, 48, unsafe.Pointer(&jitSums[0]))
_, err := k.CallFunc("fastStereoSumsAVX2", args)
runtime.KeepAlive(left)
runtime.KeepAlive(right)
runtime.KeepAlive(jitSums)
if err != nil {
t.Fatalf("iter %d: %v", iter, err)
}
if *jitSums != goSums {
t.Fatalf("iter %d: sums mismatch:\n JIT=%v\n Go =%v", iter, *jitSums, goSums)
}
}
}
func foldAbs(v int32) uint64 {
return uint64(uint32(v<<1) ^ uint32(v>>31))
}
// runAnalyzeTest is the shared harness for the analyzeO*Range family.
// analyzeJitHist is a shared histogram buffer for the analyze tests.
// It is package-level so its address is stable across all calls.
var analyzeJitHist [32]uint16
// analyzeJitSums is a shared sums buffer for the fastStereoSums test.
var analyzeJitSums [4]uint64
// analyzeSwin and analyzeJitDstP are shared input/output buffers for the
// analyze tests. Package-level so their addresses are stable.
var analyzeSwin [136]int32 // max n(128) + max order(4) = 132, rounded up
var analyzeJitDstP [128]uint32
func runAnalyzeTest(t *testing.T, k *Kernel, name string, order int, ref func([]int32, []uint32, *[32]uint16) (uint64, bool)) {
t.Helper()
rng := rand.New(rand.NewSource(int64(order)*100 + 7))
jitHist := &analyzeJitHist
swin := analyzeSwin[:0]
jitDstP := analyzeJitDstP[:0]
for iter := 0; iter < 200; iter++ {
n := 1 + rng.Intn(128)
swin = swin[:n+order]
for i := range swin {
swin[i] = int32(rng.Intn(1<<20) - 1<<19)
}
goDstP := make([]uint32, n)
var goHist [32]uint16
goSum, goOvf := ref(swin, goDstP, &goHist)
jitDstP = jitDstP[:n]
for i := range jitDstP {
jitDstP[i] = 0
}
*jitHist = [32]uint16{} // reset for this iteration
args := make([]byte, 72)
PutPtr(args, 0, unsafe.Pointer(&swin[0]))
PutUint64(args, 8, uint64(len(swin)))
PutUint64(args, 16, uint64(cap(swin)))
PutPtr(args, 24, unsafe.Pointer(&jitDstP[0]))
PutUint64(args, 32, uint64(n))
PutUint64(args, 40, uint64(cap(jitDstP)))
PutPtr(args, 48, unsafe.Pointer(&jitHist[0]))
// Memory barrier: ensure all writes above are visible to the JIT code.
runtime.KeepAlive(args)
out, err := k.CallFunc(name, args)
runtime.KeepAlive(swin)
runtime.KeepAlive(jitDstP)
runtime.KeepAlive(jitHist)
if err != nil {
t.Fatalf("iter %d: %v", iter, err)
}
jitSum := GetUint64(out, 56)
jitOvf := out[64] != 0
if jitSum != goSum {
t.Fatalf("iter %d: partSum: JIT=%d Go=%d", iter, jitSum, goSum)
}
if jitOvf != goOvf {
t.Fatalf("iter %d: overflow: JIT=%v Go=%v", iter, jitOvf, goOvf)
}
for i := range goDstP {
if jitDstP[i] != goDstP[i] {
t.Fatalf("iter %d: dstP[%d]: JIT=%d Go=%d", iter, i, jitDstP[i], goDstP[i])
}
}
if *jitHist != goHist {
t.Fatalf("iter %d: hist mismatch\n JIT=%v\n Go =%v\n n=%d", iter, *jitHist, goHist, n)
}
}
}
func TestFLACAnalyzeO2Range(t *testing.T) {
k := loadFLACKernel(t)
runAnalyzeTest(t, k, "analyzeO2RangeAVX2", 2, analyzeO2RangeGo)
}
func TestFLACAnalyzeResRange(t *testing.T) {
k := loadFLACKernel(t)
// analyzeResRange has order 0: swin IS the residual (no prediction).
runAnalyzeTest(t, k, "analyzeResRangeAVX2", 0, analyzeResRangeGo)
}
func TestFLACAnalyzeO3Range(t *testing.T) {
k := loadFLACKernel(t)
ref := func(swin []int32, dstP []uint32, hist *[32]uint16) (uint64, bool) {
swin = swin[:len(dstP)+3]
var partSum uint64
var overflow bool
for j := 0; j+3 < len(swin); j++ {
r := swin[j+3] - 3*swin[j+2] + 3*swin[j+1] - swin[j]
if r == -2147483648 {
overflow = true
}
f := uint32(r<<1) ^ uint32(r>>31)
dstP[j] = f
partSum += uint64(f)
bl := 0
for v := f; v > 0; v >>= 1 {
bl++
}
if bl > 31 {
bl = 31
}
hist[bl]++
}
return partSum, overflow
}
runAnalyzeTest(t, k, "analyzeO3RangeAVX2", 3, ref)
}
func TestFLACAnalyzeO4Range(t *testing.T) {
k := loadFLACKernel(t)
ref := func(swin []int32, dstP []uint32, hist *[32]uint16) (uint64, bool) {
swin = swin[:len(dstP)+4]
var partSum uint64
var overflow bool
for j := 0; j+4 < len(swin); j++ {
r := swin[j+4] - 4*swin[j+3] + 6*swin[j+2] - 4*swin[j+1] + swin[j]
if r == -2147483648 {
overflow = true
}
f := uint32(r<<1) ^ uint32(r>>31)
dstP[j] = f
partSum += uint64(f)
bl := 0
for v := f; v > 0; v >>= 1 {
bl++
}
if bl > 31 {
bl = 31
}
hist[bl]++
}
return partSum, overflow
}
runAnalyzeTest(t, k, "analyzeO4RangeAVX2", 4, ref)
}
func TestFLACDecodeMono24(t *testing.T) {
k := loadFLACKernel(t)
rng := rand.New(rand.NewSource(55))
src := mono24Src[:0]
jitDst := mono24JitDst[:0]
for iter := 0; iter < 500; iter++ {
n := rng.Intn(256)
src = src[:3*n]
rng.Read(src)
goDst := make([]int32, n)
decodeMono24Go(src, goDst)
jitDst = jitDst[:n]
for i := range jitDst {
jitDst[i] = 0
}
args := make([]byte, 48)
if len(src) > 0 {
PutPtr(args, 0, unsafe.Pointer(&src[0]))
}
PutUint64(args, 8, uint64(len(src)))
PutUint64(args, 16, uint64(cap(src)))
if n > 0 {
PutPtr(args, 24, unsafe.Pointer(&jitDst[0]))
}
PutUint64(args, 32, uint64(n))
PutUint64(args, 40, uint64(cap(jitDst)))
_, err := k.CallFunc("decodeMono24AVX2", args)
runtime.KeepAlive(src)
runtime.KeepAlive(jitDst)
if err != nil {
t.Fatalf("iter %d: %v", iter, err)
}
for i := range goDst {
if jitDst[i] != goDst[i] {
t.Fatalf("iter %d: dst[%d]: JIT=%d Go=%d", iter, i, jitDst[i], goDst[i])
}
}
}
}
func TestFLACDecodeStereo16(t *testing.T) {
k := loadFLACKernel(t)
rng := rand.New(rand.NewSource(66))
src := stereo16Src[:0]
jitLeft := stereo16JitLeft[:0]
jitRight := stereo16JitRight[:0]
for iter := 0; iter < 500; iter++ {
n := rng.Intn(256)
src = src[:4*n] // [L0,R0,L1,R1,...]
rng.Read(src)
goLeft := make([]int32, n)
goRight := make([]int32, n)
decodeStereo16Go(src, goLeft, goRight)
jitLeft = jitLeft[:n]
jitRight = jitRight[:n]
for i := range jitLeft {
jitLeft[i] = 0
jitRight[i] = 0
}
args := make([]byte, 72)
if len(src) > 0 {
PutPtr(args, 0, unsafe.Pointer(&src[0]))
}
PutUint64(args, 8, uint64(len(src)))
PutUint64(args, 16, uint64(cap(src)))
if n > 0 {
PutPtr(args, 24, unsafe.Pointer(&jitLeft[0]))
PutPtr(args, 48, unsafe.Pointer(&jitRight[0]))
}
PutUint64(args, 32, uint64(n))
PutUint64(args, 40, uint64(cap(jitLeft)))
PutUint64(args, 56, uint64(n))
PutUint64(args, 64, uint64(cap(jitRight)))
_, err := k.CallFunc("decodeStereo16AVX2", args)
runtime.KeepAlive(src)
runtime.KeepAlive(jitLeft)
runtime.KeepAlive(jitRight)
if err != nil {
t.Fatalf("iter %d: %v", iter, err)
}
for i := 0; i < n; i++ {
if jitLeft[i] != goLeft[i] || jitRight[i] != goRight[i] {
t.Fatalf("iter %d: [%d] L: JIT=%d Go=%d; R: JIT=%d Go=%d",
iter, i, jitLeft[i], goLeft[i], jitRight[i], goRight[i])
}
}
}
}
-165
View File
@@ -1,165 +0,0 @@
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
package verify
import (
"bytes"
"os"
"runtime"
"testing"
"unsafe"
)
// lz4KernelPath is the sibling repository's AVX2 kernel, used for
// integration testing. The test is skipped when the file is absent
// (e.g. in CI without the sibling checkout).
const lz4KernelPath = "../../go-libraries/go-lz4/avx2_amd64.s"
func loadLZ4Kernel(t *testing.T) *Kernel {
t.Helper()
if _, err := os.Stat(lz4KernelPath); err != nil {
t.Skipf("sibling kernel not available: %v", err)
}
k, err := Load(lz4KernelPath)
if err != nil {
t.Fatalf("Load(%s): %v", lz4KernelPath, err)
}
t.Cleanup(k.Close)
return k
}
// callDecodeBlockAVX2 invokes the JIT-assembled decodeBlockAVX2 with the
// given src and dst buffers, returning (n, code).
func callDecodeBlockAVX2(t *testing.T, k *Kernel, src, dst []byte) (int, int) {
t.Helper()
args := make([]byte, 64)
if len(src) > 0 {
PutPtr(args, 0, unsafe.Pointer(&src[0]))
}
PutUint64(args, 8, uint64(len(src)))
PutUint64(args, 16, uint64(cap(src)))
if len(dst) > 0 {
PutPtr(args, 24, unsafe.Pointer(&dst[0]))
}
PutUint64(args, 32, uint64(len(dst)))
PutUint64(args, 40, uint64(cap(dst)))
out, err := k.CallFunc("decodeBlockAVX2", args)
runtime.KeepAlive(src)
runtime.KeepAlive(dst)
if err != nil {
t.Fatalf("CallFunc(decodeBlockAVX2): %v", err)
}
return int(GetUint64(out, 48)), int(GetUint64(out, 56))
}
func TestLZ4DecodeKnownAnswers(t *testing.T) {
k := loadLZ4Kernel(t)
tests := []struct {
name string
src []byte
dstSize int
wantDst []byte
wantN int
wantCode int
}{
{
name: "literals_only",
src: []byte{0x50, 'H', 'e', 'l', 'l', 'o'},
dstSize: 16,
wantDst: []byte("Hello"),
wantN: 5,
wantCode: 0,
},
{
name: "literals_and_match",
src: []byte{0x54, 'A', 'A', 'A', 'A', 'A', 0x05, 0x00, 0x30, 'B', 'B', 'B'},
dstSize: 32,
wantDst: []byte("AAAAAAAAAAAAABBB"),
wantN: 16,
wantCode: 0,
},
{
name: "overlapping_match",
// 1 literal 'X', then match offset=1 length=4+4=8 → "XXXXXXXXX",
// then final 1 literal 'Y'.
src: []byte{0x14, 'X', 0x01, 0x00, 0x10, 'Y'},
dstSize: 16,
wantDst: []byte("XXXXXXXXXY"),
wantN: 10,
wantCode: 0,
},
{
name: "malformed_truncated",
src: []byte{0x50, 'H', 'e'}, // claims 5 literals, has 2
dstSize: 16,
wantN: 0,
wantCode: 1,
},
{
name: "zero_offset",
src: []byte{0x14, 'X', 0x00, 0x00},
dstSize: 16,
wantN: 0,
wantCode: 2,
},
{
name: "empty_token",
src: []byte{0x00}, // 0 literals, end of block
dstSize: 16,
wantDst: nil,
wantN: 0,
wantCode: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dst := make([]byte, tt.dstSize)
n, code := callDecodeBlockAVX2(t, k, tt.src, dst)
if n != tt.wantN || code != tt.wantCode {
t.Fatalf("decodeBlockAVX2: got (n=%d, code=%d), want (n=%d, code=%d)",
n, code, tt.wantN, tt.wantCode)
}
if tt.wantCode == 0 && tt.wantDst != nil {
if !bytes.Equal(dst[:n], tt.wantDst) {
t.Errorf("output mismatch:\n got %q\n want %q", dst[:n], tt.wantDst)
}
}
})
}
}
func TestLZ4WideCopyAVX2(t *testing.T) {
k := loadLZ4Kernel(t)
sizes := []int{0, 1, 15, 16, 31, 32, 33, 63, 64, 100, 256, 1024}
for _, n := range sizes {
src := make([]byte, n)
for i := range src {
src[i] = byte(i*13 + 7)
}
dst := make([]byte, n)
args := make([]byte, 48)
if n > 0 {
PutPtr(args, 0, unsafe.Pointer(&dst[0]))
PutPtr(args, 24, unsafe.Pointer(&src[0]))
}
PutUint64(args, 8, uint64(n))
PutUint64(args, 16, uint64(n))
PutUint64(args, 32, uint64(n))
PutUint64(args, 40, uint64(n))
_, err := k.CallFunc("wideCopyAVX2", args)
runtime.KeepAlive(dst)
runtime.KeepAlive(src)
if err != nil {
t.Fatalf("wideCopyAVX2(n=%d): %v", n, err)
}
if !bytes.Equal(dst, src) {
t.Errorf("wideCopyAVX2(n=%d): output mismatch", n)
}
}
}