146 lines
3.5 KiB
Go
146 lines
3.5 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package verify
|
|
|
|
import (
|
|
"testing"
|
|
"unsafe"
|
|
)
|
|
|
|
func loadABIKernel(t *testing.T) *Kernel {
|
|
t.Helper()
|
|
k, err := Load("../testdata/verify/abi_amd64.s")
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
t.Cleanup(k.Close)
|
|
return k
|
|
}
|
|
|
|
func TestABIClean(t *testing.T) {
|
|
k := loadABIKernel(t)
|
|
|
|
args := make([]byte, 24)
|
|
PutUint64(args, 0, 3)
|
|
PutUint64(args, 8, 4)
|
|
|
|
out, report, err := k.CallFuncChecked("cleanAdd", args)
|
|
if err != nil {
|
|
t.Fatalf("CallFuncChecked: %v", err)
|
|
}
|
|
if got := int64(GetUint64(out, 16)); got != 7 {
|
|
t.Errorf("cleanAdd(3, 4) = %d, want 7", got)
|
|
}
|
|
if !report.OK() {
|
|
t.Errorf("cleanAdd: %s", report)
|
|
}
|
|
}
|
|
|
|
func TestABIBPClobbered(t *testing.T) {
|
|
k := loadABIKernel(t)
|
|
|
|
args := make([]byte, 16)
|
|
PutUint64(args, 0, 42)
|
|
|
|
out, report, err := k.CallFuncChecked("dirtyBP", args)
|
|
if err != nil {
|
|
t.Fatalf("CallFuncChecked: %v", err)
|
|
}
|
|
if got := int64(GetUint64(out, 8)); got != 42 {
|
|
t.Errorf("dirtyBP(42) = %d, want 42", got)
|
|
}
|
|
if !report.BPClobbered {
|
|
t.Error("dirtyBP: expected BP clobbered, but report says clean")
|
|
}
|
|
if report.R14Clobbered {
|
|
t.Error("dirtyBP: R14 should not be clobbered")
|
|
}
|
|
}
|
|
|
|
func TestABIR14Clobbered(t *testing.T) {
|
|
k := loadABIKernel(t)
|
|
|
|
args := make([]byte, 16)
|
|
PutUint64(args, 0, 99)
|
|
|
|
out, report, err := k.CallFuncChecked("dirtyR14", args)
|
|
if err != nil {
|
|
t.Fatalf("CallFuncChecked: %v", err)
|
|
}
|
|
if got := int64(GetUint64(out, 8)); got != 99 {
|
|
t.Errorf("dirtyR14(99) = %d, want 99", got)
|
|
}
|
|
if !report.R14Clobbered {
|
|
t.Error("dirtyR14: expected R14 clobbered, but report says clean")
|
|
}
|
|
if report.BPClobbered {
|
|
t.Error("dirtyR14: BP should not be clobbered")
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
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)
|
|
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)
|
|
|
|
// Nonexistent function.
|
|
_, _, err := k.CallFuncChecked("nope", make([]byte, 8))
|
|
if err == nil {
|
|
t.Fatal("expected error for nonexistent function")
|
|
}
|
|
|
|
// Arg block too small.
|
|
_, _, err = k.CallFuncChecked("cleanAdd", make([]byte, 8))
|
|
if err == nil {
|
|
t.Fatal("expected error for too-small arg block")
|
|
}
|
|
}
|