test(verify): prove the riscv64 vector families through the link-parity kernel
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
ffc23929c6
commit
b22abf512f
3 files changed
+122
-1
No files matched your search
@@ -82,7 +82,7 @@ fuzz target pkg fuzztime="60s":
|
|||||||
|
|
||||||
# The cmd/link GOOBJ parity gate, opt-in: it costs minutes no pipeline can afford.
|
# The cmd/link GOOBJ parity gate, opt-in: it costs minutes no pipeline can afford.
|
||||||
link-parity:
|
link-parity:
|
||||||
systemd-run --user --scope -p MemoryMax={{memlimit}} -p MemorySwapMax=0 env GASM_LINK_PARITY=1 go test -count=1 -timeout 0 -run 'TestGOOBJLinkRegression' ./verify/
|
systemd-run --user --scope -p MemoryMax={{memlimit}} -p MemorySwapMax=0 env GASM_LINK_PARITY=1 go test -count=1 -timeout 0 -run 'TestGOOBJLink' ./verify/
|
||||||
|
|
||||||
# Benchmarks. On an idle machine only, and deliberately unfenced.
|
# Benchmarks. On an idle machine only, and deliberately unfenced.
|
||||||
bench pkgs=packages:
|
bench pkgs=packages:
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package verify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// goobjVectorMainSrc is the Go side of the vector regression. The kernel
|
||||||
|
// computes a signed byte checksum through the RVV load/store and arithmetic
|
||||||
|
// families; main derives the same value in Go and panics on any divergence
|
||||||
|
// before printing the deterministic line the baseline and the gasm-linked
|
||||||
|
// binaries must agree on.
|
||||||
|
const goobjVectorMainSrc = `package main
|
||||||
|
|
||||||
|
func checksum(buf []byte, seed int64) int64
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
buf := []byte("gasm link parity vector kernel!")
|
||||||
|
total := int64(0)
|
||||||
|
for _, b := range buf {
|
||||||
|
total += int64(int8(b))
|
||||||
|
}
|
||||||
|
want := total*0x0101010101010101 ^ 0x5eed
|
||||||
|
if got := checksum(buf, 0x5eed); got != want {
|
||||||
|
panic("checksum")
|
||||||
|
}
|
||||||
|
println("ok", checksum(buf, 0x5eed))
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// TestGOOBJLinkRVVKernel proves the riscv64 vector families merged into the
|
||||||
|
// encoder corpus are proven end to end: the kernel assembles by gasm into a
|
||||||
|
// GOOBJ object, substitutes byte-wise into a real go build archive, relinks
|
||||||
|
// with cmd/link and the linked binary computes the same checksum as the
|
||||||
|
// toolchain-built one under qemu-user. The reduction reads the live vl, so
|
||||||
|
// the checksum holds for any hardware VLEN.
|
||||||
|
func TestGOOBJLinkRVVKernel(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("builds the gasm binary and links Go programs")
|
||||||
|
}
|
||||||
|
if os.Getenv("GASM_LINK_PARITY") == "" {
|
||||||
|
t.Skip("deliberate verification: set GASM_LINK_PARITY=1 (just link-parity)")
|
||||||
|
}
|
||||||
|
goBin, err := exec.LookPath("go")
|
||||||
|
if err != nil {
|
||||||
|
t.Skip("no Go toolchain available")
|
||||||
|
}
|
||||||
|
gasmBin := buildLinkParityGasm(t, goBin)
|
||||||
|
t.Run("riscv64", func(t *testing.T) {
|
||||||
|
goobjLinkKernel(t, goBin, gasmBin, "riscv64", "linkvec_riscv64.s", goobjVectorMainSrc)
|
||||||
|
})
|
||||||
|
}
|
||||||
Vendored
+65
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// The vector link-regression kernel for riscv64: the RVV load/store and
|
||||||
|
// arithmetic families computing a checksum end to end. bytetotal
|
||||||
|
// strip-mines a byte buffer with VSETVLI, loads each chunk with VLE8V,
|
||||||
|
// widens it to E64 lanes with VSEXTVF8 and reduces the chunk with
|
||||||
|
// VREDSUMVS, so the result never depends on the hardware VLEN. checksum
|
||||||
|
// passes the slice to bytetotal through the ABI0 stack slots and mixes the
|
||||||
|
// reduction with the seed through the intra-file mix call. mix multiplies
|
||||||
|
// by the odd golden-ratio constant and XORs the seed, the same arithmetic
|
||||||
|
// the Go side of the regression mirrors bit for bit.
|
||||||
|
|
||||||
|
// func bytetotal(buf []byte) int64
|
||||||
|
TEXT ·bytetotal(SB), NOSPLIT, $0-32
|
||||||
|
MOV buf+0(FP), X10
|
||||||
|
MOV buf_len+8(FP), X12
|
||||||
|
MOV $0, X13
|
||||||
|
BEQZ X12, empty
|
||||||
|
|
||||||
|
loop:
|
||||||
|
VSETVLI X12, E64, M1, TA, MA, X11
|
||||||
|
VLE8V (X10), V16
|
||||||
|
VSEXTVF8 V16, V9
|
||||||
|
VMVVI $0, V10
|
||||||
|
VREDSUMVS V10, V9, V11
|
||||||
|
VMVXS V11, X14
|
||||||
|
ADD X14, X13, X13
|
||||||
|
ADD X11, X10, X10
|
||||||
|
SUB X11, X12, X12
|
||||||
|
BNE X12, X0, loop
|
||||||
|
|
||||||
|
empty:
|
||||||
|
MOV X13, ret+24(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func mix(x, seed int64) int64
|
||||||
|
TEXT ·mix(SB), NOSPLIT, $0-24
|
||||||
|
MOV x+0(FP), X10
|
||||||
|
MOV seed+8(FP), X11
|
||||||
|
MOV $0x0101010101010101, X12
|
||||||
|
MUL X12, X10, X10
|
||||||
|
XOR X11, X10, X10
|
||||||
|
MOV X10, ret+16(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func checksum(buf []byte, seed int64) int64
|
||||||
|
TEXT ·checksum(SB), NOSPLIT, $40-40
|
||||||
|
MOV buf+0(FP), X10
|
||||||
|
MOV buf+8(FP), X11
|
||||||
|
MOV buf+16(FP), X12
|
||||||
|
MOV X10, 8(SP)
|
||||||
|
MOV X11, 16(SP)
|
||||||
|
MOV X12, 24(SP)
|
||||||
|
CALL ·bytetotal(SB)
|
||||||
|
MOV 32(SP), X10
|
||||||
|
MOV seed+24(FP), X11
|
||||||
|
MOV X10, 8(SP)
|
||||||
|
MOV X11, 16(SP)
|
||||||
|
CALL ·mix(SB)
|
||||||
|
MOV 24(SP), X10
|
||||||
|
MOV X10, ret+32(FP)
|
||||||
|
RET
|
||||||
Reference in new issue
Block a user