Source file src/simd/archsimd/internal/simd_test/binary_sve_arm64_test.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build goexperiment.simd && arm64
     6  
     7  // SVE binary-op tests. Unlike amd64, SVE has only a handful of (scalable)
     8  // vector types, so there is nothing to generate — these drivers are hand-written
     9  // in the same shape as the generated testXxxBinary helpers. Each loads two input
    10  // windows via the fixed-array API, runs the op, stores the result, and compares
    11  // the lanes the hardware actually populated: the vector's runtime Len() (VL is
    12  // <= the 32-byte backing, enforced at package init).
    13  
    14  package simd_test
    15  
    16  import (
    17  	"math"
    18  	"simd/archsimd"
    19  	"testing"
    20  )
    21  
    22  // sveMaxBytes is the fixed backing-array size for a scalable vector: the maximum
    23  // vector length simd supports (256 bits).
    24  const sveMaxBytes = 32
    25  
    26  // testSVEBinary drives a scalable binary op like the generated testXxxBinary
    27  // helpers. active is the runtime number of live lanes (from the vector's Len()).
    28  func testSVEBinary[T number, V any](t *testing.T, pool []T, elemBytes, active int,
    29  	load func([]T) V, f func(V, V) V, store func(V, []T), want func([]T, []T) []T) {
    30  	t.Helper()
    31  	count := sveMaxBytes / elemBytes // lanes in the fixed backing array
    32  	forSlicePair(t, pool, count, func(x, y []T) bool {
    33  		t.Helper()
    34  		g := make([]T, count)
    35  		store(f(load(x), load(y)), g)
    36  		w := want(x, y)
    37  		return checkSlicesLogInput(t, g[:active], w[:active], 0.0, func() {
    38  			t.Helper()
    39  			t.Logf("x=%v", x)
    40  			t.Logf("y=%v", y)
    41  		})
    42  	})
    43  }
    44  
    45  func testInt8sBinary(t *testing.T, f func(_, _ archsimd.Int8s) archsimd.Int8s, want func(_, _ []int8) []int8) {
    46  	var z archsimd.Int8s
    47  	testSVEBinary(t, int8s, 1, z.Len(), archsimd.LoadInt8s, f, archsimd.Int8s.Store, want)
    48  }
    49  
    50  func testInt16sBinary(t *testing.T, f func(_, _ archsimd.Int16s) archsimd.Int16s, want func(_, _ []int16) []int16) {
    51  	var z archsimd.Int16s
    52  	testSVEBinary(t, int16s, 2, z.Len(), archsimd.LoadInt16s, f, archsimd.Int16s.Store, want)
    53  }
    54  
    55  func testInt32sBinary(t *testing.T, f func(_, _ archsimd.Int32s) archsimd.Int32s, want func(_, _ []int32) []int32) {
    56  	var z archsimd.Int32s
    57  	testSVEBinary(t, int32s, 4, z.Len(), archsimd.LoadInt32s, f, archsimd.Int32s.Store, want)
    58  }
    59  
    60  func testInt64sBinary(t *testing.T, f func(_, _ archsimd.Int64s) archsimd.Int64s, want func(_, _ []int64) []int64) {
    61  	var z archsimd.Int64s
    62  	testSVEBinary(t, int64s, 8, z.Len(), archsimd.LoadInt64s, f, archsimd.Int64s.Store, want)
    63  }
    64  
    65  func testUint8sBinary(t *testing.T, f func(_, _ archsimd.Uint8s) archsimd.Uint8s, want func(_, _ []uint8) []uint8) {
    66  	var z archsimd.Uint8s
    67  	testSVEBinary(t, uint8s, 1, z.Len(), archsimd.LoadUint8s, f, archsimd.Uint8s.Store, want)
    68  }
    69  
    70  func testFloat32sBinary(t *testing.T, f func(_, _ archsimd.Float32s) archsimd.Float32s, want func(_, _ []float32) []float32) {
    71  	var z archsimd.Float32s
    72  	testSVEBinary(t, float32s, 4, z.Len(), archsimd.LoadFloat32s, f, archsimd.Float32s.Store, want)
    73  }
    74  
    75  func testFloat64sBinary(t *testing.T, f func(_, _ archsimd.Float64s) archsimd.Float64s, want func(_, _ []float64) []float64) {
    76  	var z archsimd.Float64s
    77  	testSVEBinary(t, float64s, 8, z.Len(), archsimd.LoadFloat64s, f, archsimd.Float64s.Store, want)
    78  }
    79  
    80  func TestAddSVE(t *testing.T) {
    81  	if !archsimd.ARM64.SVE() {
    82  		t.Skip("no SVE")
    83  	}
    84  	testInt8sBinary(t, archsimd.Int8s.Add, addSlice[int8])
    85  	testInt16sBinary(t, archsimd.Int16s.Add, addSlice[int16])
    86  	testInt32sBinary(t, archsimd.Int32s.Add, addSlice[int32])
    87  	testInt64sBinary(t, archsimd.Int64s.Add, addSlice[int64])
    88  	testUint8sBinary(t, archsimd.Uint8s.Add, addSlice[uint8])
    89  	testFloat32sBinary(t, archsimd.Float32s.Add, addSlice[float32])
    90  	testFloat64sBinary(t, archsimd.Float64s.Add, addSlice[float64])
    91  }
    92  
    93  func TestSubSVE(t *testing.T) {
    94  	if !archsimd.ARM64.SVE() {
    95  		t.Skip("no SVE")
    96  	}
    97  	testInt8sBinary(t, archsimd.Int8s.Sub, subSlice[int8])
    98  	testInt16sBinary(t, archsimd.Int16s.Sub, subSlice[int16])
    99  	testInt32sBinary(t, archsimd.Int32s.Sub, subSlice[int32])
   100  	testInt64sBinary(t, archsimd.Int64s.Sub, subSlice[int64])
   101  	testUint8sBinary(t, archsimd.Uint8s.Sub, subSlice[uint8])
   102  	testFloat32sBinary(t, archsimd.Float32s.Sub, subSlice[float32])
   103  	testFloat64sBinary(t, archsimd.Float64s.Sub, subSlice[float64])
   104  }
   105  
   106  // testSVEUnary drives a scalable unary op, the one-input counterpart of
   107  // testSVEBinary.
   108  func testSVEUnary[T number, V any](t *testing.T, pool []T, elemBytes, active int,
   109  	load func([]T) V, f func(V) V, store func(V, []T), want func([]T) []T) {
   110  	t.Helper()
   111  	count := sveMaxBytes / elemBytes
   112  	forSlice(t, pool, count, func(x []T) bool {
   113  		t.Helper()
   114  		g := make([]T, count)
   115  		store(f(load(x)), g)
   116  		w := want(x)
   117  		return checkSlicesLogInput(t, g[:active], w[:active], 0.0, func() {
   118  			t.Helper()
   119  			t.Logf("x=%v", x)
   120  		})
   121  	})
   122  }
   123  
   124  func TestAbsSVE(t *testing.T) {
   125  	if !archsimd.ARM64.SVE() {
   126  		t.Skip("no SVE")
   127  	}
   128  	absSlice := func(x []int8) []int8 {
   129  		r := make([]int8, len(x))
   130  		for i, v := range x {
   131  			if v < 0 {
   132  				v = -v // -128 stays -128, as ABS does
   133  			}
   134  			r[i] = v
   135  		}
   136  		return r
   137  	}
   138  	var z archsimd.Int8s
   139  	testSVEUnary(t, int8s, 1, z.Len(), archsimd.LoadInt8s, archsimd.Int8s.Abs, archsimd.Int8s.Store, absSlice)
   140  	absFloat32 := func(x []float32) []float32 {
   141  		r := make([]float32, len(x))
   142  		for i, v := range x {
   143  			r[i] = float32(math.Abs(float64(v)))
   144  		}
   145  		return r
   146  	}
   147  	var zf archsimd.Float32s
   148  	testSVEUnary(t, float32s, 4, zf.Len(), archsimd.LoadFloat32s, archsimd.Float32s.Abs, archsimd.Float32s.Store, absFloat32)
   149  }
   150  
   151  func TestNegSVE(t *testing.T) {
   152  	if !archsimd.ARM64.SVE() {
   153  		t.Skip("no SVE")
   154  	}
   155  	negInt8 := func(x []int8) []int8 {
   156  		r := make([]int8, len(x))
   157  		for i, v := range x {
   158  			r[i] = -v
   159  		}
   160  		return r
   161  	}
   162  	negFloat64 := func(x []float64) []float64 {
   163  		r := make([]float64, len(x))
   164  		for i, v := range x {
   165  			r[i] = -v
   166  		}
   167  		return r
   168  	}
   169  	var zi archsimd.Int8s
   170  	testSVEUnary(t, int8s, 1, zi.Len(), archsimd.LoadInt8s, archsimd.Int8s.Neg, archsimd.Int8s.Store, negInt8)
   171  	var zf archsimd.Float64s
   172  	testSVEUnary(t, float64s, 8, zf.Len(), archsimd.LoadFloat64s, archsimd.Float64s.Neg, archsimd.Float64s.Store, negFloat64)
   173  }
   174  
   175  func TestSqrtSVE(t *testing.T) {
   176  	if !archsimd.ARM64.SVE() {
   177  		t.Skip("no SVE")
   178  	}
   179  	var in, got [4]float64
   180  	for i := range in {
   181  		in[i] = float64(i + 1)
   182  	}
   183  	v := archsimd.LoadFloat64s(in[:])
   184  	v.Sqrt().Store(got[:])
   185  	var z archsimd.Float64s
   186  	for i := 0; i < z.Len(); i++ {
   187  		if want := math.Sqrt(in[i]); got[i] != want {
   188  			t.Errorf("lane %d: Sqrt(%v) = %v, want %v", i, in[i], got[i], want)
   189  		}
   190  	}
   191  }
   192  
   193  func TestCeilSVE(t *testing.T) {
   194  	if !archsimd.ARM64.SVE() {
   195  		t.Skip("no SVE")
   196  	}
   197  	var in, got [4]float64
   198  	for i := range in {
   199  		in[i] = float64(i) - 1.5
   200  	}
   201  	v := archsimd.LoadFloat64s(in[:])
   202  	v.Ceil().Store(got[:])
   203  	var z archsimd.Float64s
   204  	for i := 0; i < z.Len(); i++ {
   205  		if want := math.Ceil(in[i]); got[i] != want {
   206  			t.Errorf("lane %d: Ceil(%v) = %v, want %v", i, in[i], got[i], want)
   207  		}
   208  	}
   209  }
   210  
   211  func TestFloorSVE(t *testing.T) {
   212  	if !archsimd.ARM64.SVE() {
   213  		t.Skip("no SVE")
   214  	}
   215  	var in, got [4]float64
   216  	for i := range in {
   217  		in[i] = float64(i) - 1.5
   218  	}
   219  	v := archsimd.LoadFloat64s(in[:])
   220  	v.Floor().Store(got[:])
   221  	var z archsimd.Float64s
   222  	for i := 0; i < z.Len(); i++ {
   223  		if want := math.Floor(in[i]); got[i] != want {
   224  			t.Errorf("lane %d: Floor(%v) = %v, want %v", i, in[i], got[i], want)
   225  		}
   226  	}
   227  }
   228  
   229  func TestTruncSVE(t *testing.T) {
   230  	if !archsimd.ARM64.SVE() {
   231  		t.Skip("no SVE")
   232  	}
   233  	var in, got [4]float64
   234  	for i := range in {
   235  		in[i] = float64(i) - 1.5
   236  	}
   237  	v := archsimd.LoadFloat64s(in[:])
   238  	v.Trunc().Store(got[:])
   239  	var z archsimd.Float64s
   240  	for i := 0; i < z.Len(); i++ {
   241  		if want := math.Trunc(in[i]); got[i] != want {
   242  			t.Errorf("lane %d: Trunc(%v) = %v, want %v", i, in[i], got[i], want)
   243  		}
   244  	}
   245  }
   246  
   247  func TestRoundSVE(t *testing.T) {
   248  	if !archsimd.ARM64.SVE() {
   249  		t.Skip("no SVE")
   250  	}
   251  	var in, got [4]float64
   252  	for i := range in {
   253  		in[i] = float64(i) - 1.5
   254  	}
   255  	v := archsimd.LoadFloat64s(in[:])
   256  	v.Round().Store(got[:])
   257  	var z archsimd.Float64s
   258  	for i := 0; i < z.Len(); i++ {
   259  		if want := math.RoundToEven(in[i]); got[i] != want {
   260  			t.Errorf("lane %d: Round(%v) = %v, want %v", i, in[i], got[i], want)
   261  		}
   262  	}
   263  }
   264  
   265  func TestAndSVE(t *testing.T) {
   266  	if !archsimd.ARM64.SVE() {
   267  		t.Skip("no SVE")
   268  	}
   269  	andInt8 := func(x, y []int8) []int8 {
   270  		r := make([]int8, len(x))
   271  		for i := range x {
   272  			r[i] = x[i] & y[i]
   273  		}
   274  		return r
   275  	}
   276  	testInt8sBinary(t, archsimd.Int8s.And, andInt8)
   277  	andUint64 := func(x, y []uint64) []uint64 {
   278  		r := make([]uint64, len(x))
   279  		for i := range x {
   280  			r[i] = x[i] & y[i]
   281  		}
   282  		return r
   283  	}
   284  	var z archsimd.Uint64s
   285  	testSVEBinary(t, uint64s, 8, z.Len(), archsimd.LoadUint64s, archsimd.Uint64s.And, archsimd.Uint64s.Store, andUint64)
   286  }
   287  
   288  func TestOrSVE(t *testing.T) {
   289  	if !archsimd.ARM64.SVE() {
   290  		t.Skip("no SVE")
   291  	}
   292  	orInt8 := func(x, y []int8) []int8 {
   293  		r := make([]int8, len(x))
   294  		for i := range x {
   295  			r[i] = x[i] | y[i]
   296  		}
   297  		return r
   298  	}
   299  	testInt8sBinary(t, archsimd.Int8s.Or, orInt8)
   300  	orUint64 := func(x, y []uint64) []uint64 {
   301  		r := make([]uint64, len(x))
   302  		for i := range x {
   303  			r[i] = x[i] | y[i]
   304  		}
   305  		return r
   306  	}
   307  	var z archsimd.Uint64s
   308  	testSVEBinary(t, uint64s, 8, z.Len(), archsimd.LoadUint64s, archsimd.Uint64s.Or, archsimd.Uint64s.Store, orUint64)
   309  }
   310  
   311  func TestXorSVE(t *testing.T) {
   312  	if !archsimd.ARM64.SVE() {
   313  		t.Skip("no SVE")
   314  	}
   315  	xorInt8 := func(x, y []int8) []int8 {
   316  		r := make([]int8, len(x))
   317  		for i := range x {
   318  			r[i] = x[i] ^ y[i]
   319  		}
   320  		return r
   321  	}
   322  	testInt8sBinary(t, archsimd.Int8s.Xor, xorInt8)
   323  }
   324  
   325  func TestAndNotSVE(t *testing.T) {
   326  	if !archsimd.ARM64.SVE() {
   327  		t.Skip("no SVE")
   328  	}
   329  	andNotInt8 := func(x, y []int8) []int8 {
   330  		r := make([]int8, len(x))
   331  		for i := range x {
   332  			r[i] = x[i] &^ y[i]
   333  		}
   334  		return r
   335  	}
   336  	testInt8sBinary(t, archsimd.Int8s.AndNot, andNotInt8)
   337  }
   338  

View as plain text