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  	"simd/archsimd"
    18  	"testing"
    19  )
    20  
    21  // sveMaxBytes is the fixed backing-array size for a scalable vector: the maximum
    22  // vector length simd supports (256 bits).
    23  const sveMaxBytes = 32
    24  
    25  // testSVEBinary drives a scalable binary op like the generated testXxxBinary
    26  // helpers. active is the runtime number of live lanes (from the vector's Len()).
    27  func testSVEBinary[T number, V any](t *testing.T, pool []T, elemBytes, active int,
    28  	load func([]T) V, f func(V, V) V, store func(V, []T), want func([]T, []T) []T) {
    29  	t.Helper()
    30  	count := sveMaxBytes / elemBytes // lanes in the fixed backing array
    31  	forSlicePair(t, pool, count, func(x, y []T) bool {
    32  		t.Helper()
    33  		g := make([]T, count)
    34  		store(f(load(x), load(y)), g)
    35  		w := want(x, y)
    36  		return checkSlicesLogInput(t, g[:active], w[:active], 0.0, func() {
    37  			t.Helper()
    38  			t.Logf("x=%v", x)
    39  			t.Logf("y=%v", y)
    40  		})
    41  	})
    42  }
    43  
    44  func testInt8sBinary(t *testing.T, f func(_, _ archsimd.Int8s) archsimd.Int8s, want func(_, _ []int8) []int8) {
    45  	var z archsimd.Int8s
    46  	testSVEBinary(t, int8s, 1, z.Len(), archsimd.LoadInt8s, f, archsimd.Int8s.Store, want)
    47  }
    48  
    49  func testInt16sBinary(t *testing.T, f func(_, _ archsimd.Int16s) archsimd.Int16s, want func(_, _ []int16) []int16) {
    50  	var z archsimd.Int16s
    51  	testSVEBinary(t, int16s, 2, z.Len(), archsimd.LoadInt16s, f, archsimd.Int16s.Store, want)
    52  }
    53  
    54  func testInt32sBinary(t *testing.T, f func(_, _ archsimd.Int32s) archsimd.Int32s, want func(_, _ []int32) []int32) {
    55  	var z archsimd.Int32s
    56  	testSVEBinary(t, int32s, 4, z.Len(), archsimd.LoadInt32s, f, archsimd.Int32s.Store, want)
    57  }
    58  
    59  func testInt64sBinary(t *testing.T, f func(_, _ archsimd.Int64s) archsimd.Int64s, want func(_, _ []int64) []int64) {
    60  	var z archsimd.Int64s
    61  	testSVEBinary(t, int64s, 8, z.Len(), archsimd.LoadInt64s, f, archsimd.Int64s.Store, want)
    62  }
    63  
    64  func testUint8sBinary(t *testing.T, f func(_, _ archsimd.Uint8s) archsimd.Uint8s, want func(_, _ []uint8) []uint8) {
    65  	var z archsimd.Uint8s
    66  	testSVEBinary(t, uint8s, 1, z.Len(), archsimd.LoadUint8s, f, archsimd.Uint8s.Store, want)
    67  }
    68  
    69  func testFloat32sBinary(t *testing.T, f func(_, _ archsimd.Float32s) archsimd.Float32s, want func(_, _ []float32) []float32) {
    70  	var z archsimd.Float32s
    71  	testSVEBinary(t, float32s, 4, z.Len(), archsimd.LoadFloat32s, f, archsimd.Float32s.Store, want)
    72  }
    73  
    74  func testFloat64sBinary(t *testing.T, f func(_, _ archsimd.Float64s) archsimd.Float64s, want func(_, _ []float64) []float64) {
    75  	var z archsimd.Float64s
    76  	testSVEBinary(t, float64s, 8, z.Len(), archsimd.LoadFloat64s, f, archsimd.Float64s.Store, want)
    77  }
    78  
    79  func testUint16sBinary(t *testing.T, f func(_, _ archsimd.Uint16s) archsimd.Uint16s, want func(_, _ []uint16) []uint16) {
    80  	var z archsimd.Uint16s
    81  	testSVEBinary(t, uint16s, 2, z.Len(), archsimd.LoadUint16s, f, archsimd.Uint16s.Store, want)
    82  }
    83  
    84  func testUint32sBinary(t *testing.T, f func(_, _ archsimd.Uint32s) archsimd.Uint32s, want func(_, _ []uint32) []uint32) {
    85  	var z archsimd.Uint32s
    86  	testSVEBinary(t, uint32s, 4, z.Len(), archsimd.LoadUint32s, f, archsimd.Uint32s.Store, want)
    87  }
    88  
    89  func testUint64sBinary(t *testing.T, f func(_, _ archsimd.Uint64s) archsimd.Uint64s, want func(_, _ []uint64) []uint64) {
    90  	var z archsimd.Uint64s
    91  	testSVEBinary(t, uint64s, 8, z.Len(), archsimd.LoadUint64s, f, archsimd.Uint64s.Store, want)
    92  }
    93  
    94  func testInt8sUnary(t *testing.T, f func(archsimd.Int8s) archsimd.Int8s, want func([]int8) []int8) {
    95  	var z archsimd.Int8s
    96  	testSVEUnary(t, int8s, 1, z.Len(), archsimd.LoadInt8s, f, archsimd.Int8s.Store, want)
    97  }
    98  
    99  func testInt16sUnary(t *testing.T, f func(archsimd.Int16s) archsimd.Int16s, want func([]int16) []int16) {
   100  	var z archsimd.Int16s
   101  	testSVEUnary(t, int16s, 2, z.Len(), archsimd.LoadInt16s, f, archsimd.Int16s.Store, want)
   102  }
   103  
   104  func testInt32sUnary(t *testing.T, f func(archsimd.Int32s) archsimd.Int32s, want func([]int32) []int32) {
   105  	var z archsimd.Int32s
   106  	testSVEUnary(t, int32s, 4, z.Len(), archsimd.LoadInt32s, f, archsimd.Int32s.Store, want)
   107  }
   108  
   109  func testInt64sUnary(t *testing.T, f func(archsimd.Int64s) archsimd.Int64s, want func([]int64) []int64) {
   110  	var z archsimd.Int64s
   111  	testSVEUnary(t, int64s, 8, z.Len(), archsimd.LoadInt64s, f, archsimd.Int64s.Store, want)
   112  }
   113  
   114  func testUint8sUnary(t *testing.T, f func(archsimd.Uint8s) archsimd.Uint8s, want func([]uint8) []uint8) {
   115  	var z archsimd.Uint8s
   116  	testSVEUnary(t, uint8s, 1, z.Len(), archsimd.LoadUint8s, f, archsimd.Uint8s.Store, want)
   117  }
   118  
   119  func testUint16sUnary(t *testing.T, f func(archsimd.Uint16s) archsimd.Uint16s, want func([]uint16) []uint16) {
   120  	var z archsimd.Uint16s
   121  	testSVEUnary(t, uint16s, 2, z.Len(), archsimd.LoadUint16s, f, archsimd.Uint16s.Store, want)
   122  }
   123  
   124  func testUint32sUnary(t *testing.T, f func(archsimd.Uint32s) archsimd.Uint32s, want func([]uint32) []uint32) {
   125  	var z archsimd.Uint32s
   126  	testSVEUnary(t, uint32s, 4, z.Len(), archsimd.LoadUint32s, f, archsimd.Uint32s.Store, want)
   127  }
   128  
   129  func testUint64sUnary(t *testing.T, f func(archsimd.Uint64s) archsimd.Uint64s, want func([]uint64) []uint64) {
   130  	var z archsimd.Uint64s
   131  	testSVEUnary(t, uint64s, 8, z.Len(), archsimd.LoadUint64s, f, archsimd.Uint64s.Store, want)
   132  }
   133  
   134  func testFloat32sUnary(t *testing.T, f func(archsimd.Float32s) archsimd.Float32s, want func([]float32) []float32) {
   135  	var z archsimd.Float32s
   136  	testSVEUnary(t, float32s, 4, z.Len(), archsimd.LoadFloat32s, f, archsimd.Float32s.Store, want)
   137  }
   138  
   139  func testFloat64sUnary(t *testing.T, f func(archsimd.Float64s) archsimd.Float64s, want func([]float64) []float64) {
   140  	var z archsimd.Float64s
   141  	testSVEUnary(t, float64s, 8, z.Len(), archsimd.LoadFloat64s, f, archsimd.Float64s.Store, want)
   142  }
   143  
   144  func TestAddSVE(t *testing.T) {
   145  	if !archsimd.ARM64.SVE() {
   146  		t.Skip("no SVE")
   147  	}
   148  	testInt8sBinary(t, archsimd.Int8s.Add, addSlice[int8])
   149  	testInt16sBinary(t, archsimd.Int16s.Add, addSlice[int16])
   150  	testInt32sBinary(t, archsimd.Int32s.Add, addSlice[int32])
   151  	testInt64sBinary(t, archsimd.Int64s.Add, addSlice[int64])
   152  	testUint8sBinary(t, archsimd.Uint8s.Add, addSlice[uint8])
   153  	testUint16sBinary(t, archsimd.Uint16s.Add, addSlice[uint16])
   154  	testUint32sBinary(t, archsimd.Uint32s.Add, addSlice[uint32])
   155  	testUint64sBinary(t, archsimd.Uint64s.Add, addSlice[uint64])
   156  	testFloat32sBinary(t, archsimd.Float32s.Add, addSlice[float32])
   157  	testFloat64sBinary(t, archsimd.Float64s.Add, addSlice[float64])
   158  }
   159  
   160  func TestSubSVE(t *testing.T) {
   161  	if !archsimd.ARM64.SVE() {
   162  		t.Skip("no SVE")
   163  	}
   164  	testInt8sBinary(t, archsimd.Int8s.Sub, subSlice[int8])
   165  	testInt16sBinary(t, archsimd.Int16s.Sub, subSlice[int16])
   166  	testInt32sBinary(t, archsimd.Int32s.Sub, subSlice[int32])
   167  	testInt64sBinary(t, archsimd.Int64s.Sub, subSlice[int64])
   168  	testUint8sBinary(t, archsimd.Uint8s.Sub, subSlice[uint8])
   169  	testUint16sBinary(t, archsimd.Uint16s.Sub, subSlice[uint16])
   170  	testUint32sBinary(t, archsimd.Uint32s.Sub, subSlice[uint32])
   171  	testUint64sBinary(t, archsimd.Uint64s.Sub, subSlice[uint64])
   172  	testFloat32sBinary(t, archsimd.Float32s.Sub, subSlice[float32])
   173  	testFloat64sBinary(t, archsimd.Float64s.Sub, subSlice[float64])
   174  }
   175  
   176  // testSVEUnary drives a scalable unary op, the one-input counterpart of
   177  // testSVEBinary.
   178  func testSVEUnary[T number, V any](t *testing.T, pool []T, elemBytes, active int,
   179  	load func([]T) V, f func(V) V, store func(V, []T), want func([]T) []T) {
   180  	t.Helper()
   181  	count := sveMaxBytes / elemBytes
   182  	forSlice(t, pool, count, func(x []T) bool {
   183  		t.Helper()
   184  		g := make([]T, count)
   185  		store(f(load(x)), g)
   186  		w := want(x)
   187  		return checkSlicesLogInput(t, g[:active], w[:active], 0.0, func() {
   188  			t.Helper()
   189  			t.Logf("x=%v", x)
   190  		})
   191  	})
   192  }
   193  
   194  func TestAbsSVE(t *testing.T) {
   195  	if !archsimd.ARM64.SVE() {
   196  		t.Skip("no SVE")
   197  	}
   198  	testInt8sUnary(t, archsimd.Int8s.Abs, absSlice[int8])
   199  	testInt16sUnary(t, archsimd.Int16s.Abs, absSlice[int16])
   200  	testInt32sUnary(t, archsimd.Int32s.Abs, absSlice[int32])
   201  	testInt64sUnary(t, archsimd.Int64s.Abs, absSlice[int64])
   202  	testFloat32sUnary(t, archsimd.Float32s.Abs, absSlice[float32])
   203  	testFloat64sUnary(t, archsimd.Float64s.Abs, absSlice[float64])
   204  }
   205  
   206  func TestNegSVE(t *testing.T) {
   207  	if !archsimd.ARM64.SVE() {
   208  		t.Skip("no SVE")
   209  	}
   210  	testInt8sUnary(t, archsimd.Int8s.Neg, negSlice[int8])
   211  	testInt16sUnary(t, archsimd.Int16s.Neg, negSlice[int16])
   212  	testInt32sUnary(t, archsimd.Int32s.Neg, negSlice[int32])
   213  	testInt64sUnary(t, archsimd.Int64s.Neg, negSlice[int64])
   214  	testFloat32sUnary(t, archsimd.Float32s.Neg, negSlice[float32])
   215  	testFloat64sUnary(t, archsimd.Float64s.Neg, negSlice[float64])
   216  }
   217  
   218  func TestSqrtSVE(t *testing.T) {
   219  	if !archsimd.ARM64.SVE() {
   220  		t.Skip("no SVE")
   221  	}
   222  	testFloat32sUnary(t, archsimd.Float32s.Sqrt, sqrtSlice[float32])
   223  	testFloat64sUnary(t, archsimd.Float64s.Sqrt, sqrtSlice[float64])
   224  }
   225  
   226  func TestCeilSVE(t *testing.T) {
   227  	if !archsimd.ARM64.SVE() {
   228  		t.Skip("no SVE")
   229  	}
   230  	testFloat32sUnary(t, archsimd.Float32s.Ceil, ceilSlice[float32])
   231  	testFloat64sUnary(t, archsimd.Float64s.Ceil, ceilSlice[float64])
   232  }
   233  
   234  func TestFloorSVE(t *testing.T) {
   235  	if !archsimd.ARM64.SVE() {
   236  		t.Skip("no SVE")
   237  	}
   238  	testFloat32sUnary(t, archsimd.Float32s.Floor, floorSlice[float32])
   239  	testFloat64sUnary(t, archsimd.Float64s.Floor, floorSlice[float64])
   240  }
   241  
   242  func TestTruncSVE(t *testing.T) {
   243  	if !archsimd.ARM64.SVE() {
   244  		t.Skip("no SVE")
   245  	}
   246  	testFloat32sUnary(t, archsimd.Float32s.Trunc, truncSlice[float32])
   247  	testFloat64sUnary(t, archsimd.Float64s.Trunc, truncSlice[float64])
   248  }
   249  
   250  func TestRoundSVE(t *testing.T) {
   251  	if !archsimd.ARM64.SVE() {
   252  		t.Skip("no SVE")
   253  	}
   254  	testFloat32sUnary(t, archsimd.Float32s.Round, roundSlice[float32])
   255  	testFloat64sUnary(t, archsimd.Float64s.Round, roundSlice[float64])
   256  }
   257  
   258  func TestAndSVE(t *testing.T) {
   259  	if !archsimd.ARM64.SVE() {
   260  		t.Skip("no SVE")
   261  	}
   262  	testInt8sBinary(t, archsimd.Int8s.And, andSlice[int8])
   263  	testInt16sBinary(t, archsimd.Int16s.And, andSlice[int16])
   264  	testInt32sBinary(t, archsimd.Int32s.And, andSlice[int32])
   265  	testInt64sBinary(t, archsimd.Int64s.And, andSlice[int64])
   266  	testUint8sBinary(t, archsimd.Uint8s.And, andSlice[uint8])
   267  	testUint16sBinary(t, archsimd.Uint16s.And, andSlice[uint16])
   268  	testUint32sBinary(t, archsimd.Uint32s.And, andSlice[uint32])
   269  	testUint64sBinary(t, archsimd.Uint64s.And, andSlice[uint64])
   270  }
   271  
   272  func TestOrSVE(t *testing.T) {
   273  	if !archsimd.ARM64.SVE() {
   274  		t.Skip("no SVE")
   275  	}
   276  	testInt8sBinary(t, archsimd.Int8s.Or, orSlice[int8])
   277  	testInt16sBinary(t, archsimd.Int16s.Or, orSlice[int16])
   278  	testInt32sBinary(t, archsimd.Int32s.Or, orSlice[int32])
   279  	testInt64sBinary(t, archsimd.Int64s.Or, orSlice[int64])
   280  	testUint8sBinary(t, archsimd.Uint8s.Or, orSlice[uint8])
   281  	testUint16sBinary(t, archsimd.Uint16s.Or, orSlice[uint16])
   282  	testUint32sBinary(t, archsimd.Uint32s.Or, orSlice[uint32])
   283  	testUint64sBinary(t, archsimd.Uint64s.Or, orSlice[uint64])
   284  }
   285  
   286  func TestXorSVE(t *testing.T) {
   287  	if !archsimd.ARM64.SVE() {
   288  		t.Skip("no SVE")
   289  	}
   290  	testInt8sBinary(t, archsimd.Int8s.Xor, xorSlice[int8])
   291  	testInt16sBinary(t, archsimd.Int16s.Xor, xorSlice[int16])
   292  	testInt32sBinary(t, archsimd.Int32s.Xor, xorSlice[int32])
   293  	testInt64sBinary(t, archsimd.Int64s.Xor, xorSlice[int64])
   294  	testUint8sBinary(t, archsimd.Uint8s.Xor, xorSlice[uint8])
   295  	testUint16sBinary(t, archsimd.Uint16s.Xor, xorSlice[uint16])
   296  	testUint32sBinary(t, archsimd.Uint32s.Xor, xorSlice[uint32])
   297  	testUint64sBinary(t, archsimd.Uint64s.Xor, xorSlice[uint64])
   298  }
   299  
   300  func TestAndNotSVE(t *testing.T) {
   301  	if !archsimd.ARM64.SVE() {
   302  		t.Skip("no SVE")
   303  	}
   304  	testInt8sBinary(t, archsimd.Int8s.AndNot, andNotSlice[int8])
   305  	testInt16sBinary(t, archsimd.Int16s.AndNot, andNotSlice[int16])
   306  	testInt32sBinary(t, archsimd.Int32s.AndNot, andNotSlice[int32])
   307  	testInt64sBinary(t, archsimd.Int64s.AndNot, andNotSlice[int64])
   308  	testUint8sBinary(t, archsimd.Uint8s.AndNot, andNotSlice[uint8])
   309  	testUint16sBinary(t, archsimd.Uint16s.AndNot, andNotSlice[uint16])
   310  	testUint32sBinary(t, archsimd.Uint32s.AndNot, andNotSlice[uint32])
   311  	testUint64sBinary(t, archsimd.Uint64s.AndNot, andNotSlice[uint64])
   312  }
   313  
   314  func TestMulSVE(t *testing.T) {
   315  	if !archsimd.ARM64.SVE() {
   316  		t.Skip("no SVE")
   317  	}
   318  	testFloat32sBinary(t, archsimd.Float32s.Mul, mulSlice[float32])
   319  	testFloat64sBinary(t, archsimd.Float64s.Mul, mulSlice[float64])
   320  	// Ungated: integer Mul compiles to the merging-predicated fallback,
   321  	// correct on any SVE.
   322  	testInt8sBinary(t, archsimd.Int8s.Mul, mulSlice[int8])
   323  	testInt16sBinary(t, archsimd.Int16s.Mul, mulSlice[int16])
   324  	testInt32sBinary(t, archsimd.Int32s.Mul, mulSlice[int32])
   325  	testInt64sBinary(t, archsimd.Int64s.Mul, mulSlice[int64])
   326  	testUint8sBinary(t, archsimd.Uint8s.Mul, mulSlice[uint8])
   327  	testUint16sBinary(t, archsimd.Uint16s.Mul, mulSlice[uint16])
   328  	testUint32sBinary(t, archsimd.Uint32s.Mul, mulSlice[uint32])
   329  	testUint64sBinary(t, archsimd.Uint64s.Mul, mulSlice[uint64])
   330  	if archsimd.ARM64.SVE2() {
   331  		// Gated: this block compiles to the unpredicated SVE2 encoding.
   332  		testInt8sBinary(t, archsimd.Int8s.Mul, mulSlice[int8])
   333  		testInt16sBinary(t, archsimd.Int16s.Mul, mulSlice[int16])
   334  		testInt32sBinary(t, archsimd.Int32s.Mul, mulSlice[int32])
   335  		testInt64sBinary(t, archsimd.Int64s.Mul, mulSlice[int64])
   336  		testUint8sBinary(t, archsimd.Uint8s.Mul, mulSlice[uint8])
   337  		testUint16sBinary(t, archsimd.Uint16s.Mul, mulSlice[uint16])
   338  		testUint32sBinary(t, archsimd.Uint32s.Mul, mulSlice[uint32])
   339  		testUint64sBinary(t, archsimd.Uint64s.Mul, mulSlice[uint64])
   340  	}
   341  }
   342  
   343  func TestMulHighSVE(t *testing.T) {
   344  	if !archsimd.ARM64.SVE() {
   345  		t.Skip("no SVE")
   346  	}
   347  	// Ungated: compiles to the merging-predicated fallback, correct on any SVE.
   348  	testInt8sBinary(t, archsimd.Int8s.MulHigh, mulHighSlice[int8])
   349  	testInt16sBinary(t, archsimd.Int16s.MulHigh, mulHighSlice[int16])
   350  	testInt32sBinary(t, archsimd.Int32s.MulHigh, mulHighSlice[int32])
   351  	testInt64sBinary(t, archsimd.Int64s.MulHigh, mulHighSlice[int64])
   352  	testUint8sBinary(t, archsimd.Uint8s.MulHigh, mulHighSlice[uint8])
   353  	testUint16sBinary(t, archsimd.Uint16s.MulHigh, mulHighSlice[uint16])
   354  	testUint32sBinary(t, archsimd.Uint32s.MulHigh, mulHighSlice[uint32])
   355  	testUint64sBinary(t, archsimd.Uint64s.MulHigh, mulHighSlice[uint64])
   356  	if archsimd.ARM64.SVE2() {
   357  		// Gated: this block compiles to the unpredicated SVE2 encodings.
   358  		testInt8sBinary(t, archsimd.Int8s.MulHigh, mulHighSlice[int8])
   359  		testInt16sBinary(t, archsimd.Int16s.MulHigh, mulHighSlice[int16])
   360  		testInt32sBinary(t, archsimd.Int32s.MulHigh, mulHighSlice[int32])
   361  		testInt64sBinary(t, archsimd.Int64s.MulHigh, mulHighSlice[int64])
   362  		testUint8sBinary(t, archsimd.Uint8s.MulHigh, mulHighSlice[uint8])
   363  		testUint16sBinary(t, archsimd.Uint16s.MulHigh, mulHighSlice[uint16])
   364  		testUint32sBinary(t, archsimd.Uint32s.MulHigh, mulHighSlice[uint32])
   365  		testUint64sBinary(t, archsimd.Uint64s.MulHigh, mulHighSlice[uint64])
   366  	}
   367  }
   368  

View as plain text