diff --git a/cmd/gasm/main.go b/cmd/gasm/main.go index 5e2a53e..4f3cd1a 100644 --- a/cmd/gasm/main.go +++ b/cmd/gasm/main.go @@ -29,7 +29,7 @@ import ( // version is the release version, stamped at build time via // -ldflags "-X main.version=…" (defaulting to the current release). -var version = "0.23.0" +var version = "0.24.0" func main() { if len(os.Args) < 2 { diff --git a/justfile b/justfile index 118df8e..4f44be8 100644 --- a/justfile +++ b/justfile @@ -3,7 +3,7 @@ # gasm-devkit — developer tooling for Go's Plan 9 assembler (GAsm). -version := "0.23.0" +version := "0.24.0" default: @just --list diff --git a/verify/flac_test.go b/verify/flac_test.go index 63dc3b6..f2e789a 100644 --- a/verify/flac_test.go +++ b/verify/flac_test.go @@ -148,6 +148,13 @@ func decodeMono24Go(src []byte, dst []int32) { } } +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 --- func TestFLACDecodeMono16(t *testing.T) { @@ -441,6 +448,62 @@ func TestFLACAnalyzeResRange(t *testing.T) { 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)) @@ -477,3 +540,46 @@ func TestFLACDecodeMono24(t *testing.T) { } } } + +func TestFLACDecodeStereo16(t *testing.T) { + k := loadFLACKernel(t) + rng := rand.New(rand.NewSource(66)) + + for iter := 0; iter < 500; iter++ { + n := rng.Intn(256) + src := make([]byte, 4*n) // [L0,R0,L1,R1,...] + rng.Read(src) + + goLeft := make([]int32, n) + goRight := make([]int32, n) + decodeStereo16Go(src, goLeft, goRight) + + jitLeft := make([]int32, n) + jitRight := make([]int32, n) + 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) + 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]) + } + } + } +}