blob: e564bd54b292997ebbc31fb533158cc85ce8b4bf [file]
// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build goexperiment.simd && arm64
package simd_test
import (
"math"
"simd/archsimd"
"testing"
)
// testSVEReduceSum checks a float ReduceSum. The values are small integers,
// so every partial sum is exact and the result does not depend on the order
// FADDV adds the lanes in.
func testSVEReduceSum[T float32 | float64, V any](t *testing.T, name string, n int, load func([]T) V, sum func(V) T) {
t.Helper()
xs := make([]T, n)
for start := -20; start <= 20; start += 7 {
var want T
for i := range xs {
xs[i] = T(start + 3*i)
want += xs[i]
}
if got := sum(load(xs)); got != want {
t.Errorf("%s(%v) = %v, want %v", name, xs, got, want)
}
}
inf := T(math.Inf(1))
for _, c := range []struct {
lane int
val T
isNaN bool
wantInf bool
}{
{0, T(math.NaN()), true, false},
{n - 1, T(math.NaN()), true, false},
{n / 2, inf, false, true},
} {
for i := range xs {
xs[i] = 1
}
xs[c.lane] = c.val
got := sum(load(xs))
switch {
case c.isNaN && got == got:
t.Errorf("%s with NaN in lane %d = %v, want NaN", name, c.lane, got)
case c.wantInf && got != inf:
t.Errorf("%s with +Inf in lane %d = %v, want +Inf", name, c.lane, got)
}
}
}
func TestReduceSumSVE(t *testing.T) {
if !archsimd.ARM64.SVE() {
t.Skip("no SVE")
}
testSVEReduceSum(t, "Float32s.ReduceSum", archsimd.Float32s{}.Len(), archsimd.LoadFloat32s, archsimd.Float32s.ReduceSum)
testSVEReduceSum(t, "Float64s.ReduceSum", archsimd.Float64s{}.Len(), archsimd.LoadFloat64s, archsimd.Float64s.ReduceSum)
}
// testSVEReduceMax checks a float ReduceMax with the maximum in each lane,
// with NaN, and with signed zeros.
func testSVEReduceMax[T float32 | float64, V any](t *testing.T, name string, n int, load func([]T) V, reduce func(V) T) {
t.Helper()
xs := make([]T, n)
for lane := range xs {
for i := range xs {
xs[i] = T(-3*i - 1)
}
xs[lane] = 5
if got := reduce(load(xs)); got != 5 {
t.Errorf("%s(%v) = %v, want 5", name, xs, got)
}
}
for i := range xs {
xs[i] = T(math.Inf(-1))
}
if got := reduce(load(xs)); got != T(math.Inf(-1)) {
t.Errorf("%s(all -Inf) = %v, want -Inf", name, got)
}
for _, lane := range []int{0, n / 2, n - 1} {
for i := range xs {
xs[i] = T(i)
}
xs[lane] = T(math.NaN())
if got := reduce(load(xs)); got == got {
t.Errorf("%s with NaN in lane %d = %v, want NaN", name, lane, got)
}
}
negZero := T(math.Copysign(0, -1))
for _, lane := range []int{0, n - 1} {
for i := range xs {
xs[i] = negZero
}
xs[lane] = 0
if got := reduce(load(xs)); got != 0 || math.Signbit(float64(got)) {
t.Errorf("%s of signed zeros with +0 in lane %d = %v, want +0", name, lane, got)
}
}
}
func TestReduceMaxSVE(t *testing.T) {
if !archsimd.ARM64.SVE() {
t.Skip("no SVE")
}
testSVEReduceMax(t, "Float32s.ReduceMax", archsimd.Float32s{}.Len(), archsimd.LoadFloat32s, archsimd.Float32s.ReduceMax)
testSVEReduceMax(t, "Float64s.ReduceMax", archsimd.Float64s{}.Len(), archsimd.LoadFloat64s, archsimd.Float64s.ReduceMax)
}