| // 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) | |
| } |