Source file src/simd/archsimd/internal/simd_test/compare_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 mask (predicate) tests, in the same utility-function shape as
     8  // compare_amd64_test.go.
     9  
    10  package simd_test
    11  
    12  import (
    13  	"simd/archsimd"
    14  	"testing"
    15  )
    16  
    17  const sveMaskUint16s = sveMaxBytes / 8
    18  
    19  // testSVECompare drives a scalable compare that returns a mask. active is the
    20  // runtime number of live lanes; elemBytes is the element width. Lane i is true
    21  // iff bit i*elemBytes of the stored predicate is set.
    22  func testSVECompare[T number, V, M any](t *testing.T, pool []T, elemBytes, active int,
    23  	load func([]T) V, cmp func(V, V) M, store func(M, []uint16), want func(T, T) bool) {
    24  	t.Helper()
    25  	count := sveMaxBytes / elemBytes
    26  	forSlicePair(t, pool, count, func(x, y []T) bool {
    27  		t.Helper()
    28  		bits := make([]uint16, sveMaskUint16s)
    29  		store(cmp(load(x), load(y)), bits)
    30  		for i := 0; i < active; i++ {
    31  			b := i * elemBytes
    32  			got := bits[b/16]>>uint(b%16)&1 == 1
    33  			if got != want(x[i], y[i]) {
    34  				t.Errorf("lane %d: got %v, want %v (x=%v y=%v)", i, got, want(x[i], y[i]), x[i], y[i])
    35  				return false
    36  			}
    37  		}
    38  		return true
    39  	})
    40  }
    41  
    42  func testInt8sCompare(t *testing.T, cmp func(_, _ archsimd.Int8s) archsimd.Mask8s, want func(_, _ int8) bool) {
    43  	var z archsimd.Int8s
    44  	testSVECompare(t, int8s, 1, z.Len(), archsimd.LoadInt8s, cmp, archsimd.Mask8s.Store, want)
    45  }
    46  
    47  func testInt16sCompare(t *testing.T, cmp func(_, _ archsimd.Int16s) archsimd.Mask16s, want func(_, _ int16) bool) {
    48  	var z archsimd.Int16s
    49  	testSVECompare(t, int16s, 2, z.Len(), archsimd.LoadInt16s, cmp, archsimd.Mask16s.Store, want)
    50  }
    51  
    52  func testInt32sCompare(t *testing.T, cmp func(_, _ archsimd.Int32s) archsimd.Mask32s, want func(_, _ int32) bool) {
    53  	var z archsimd.Int32s
    54  	testSVECompare(t, int32s, 4, z.Len(), archsimd.LoadInt32s, cmp, archsimd.Mask32s.Store, want)
    55  }
    56  
    57  func testInt64sCompare(t *testing.T, cmp func(_, _ archsimd.Int64s) archsimd.Mask64s, want func(_, _ int64) bool) {
    58  	var z archsimd.Int64s
    59  	testSVECompare(t, int64s, 8, z.Len(), archsimd.LoadInt64s, cmp, archsimd.Mask64s.Store, want)
    60  }
    61  
    62  func testUint8sCompare(t *testing.T, cmp func(_, _ archsimd.Uint8s) archsimd.Mask8s, want func(_, _ uint8) bool) {
    63  	var z archsimd.Uint8s
    64  	testSVECompare(t, uint8s, 1, z.Len(), archsimd.LoadUint8s, cmp, archsimd.Mask8s.Store, want)
    65  }
    66  
    67  func testFloat32sCompare(t *testing.T, cmp func(_, _ archsimd.Float32s) archsimd.Mask32s, want func(_, _ float32) bool) {
    68  	var z archsimd.Float32s
    69  	testSVECompare(t, float32s, 4, z.Len(), archsimd.LoadFloat32s, cmp, archsimd.Mask32s.Store, want)
    70  }
    71  
    72  func testFloat64sCompare(t *testing.T, cmp func(_, _ archsimd.Float64s) archsimd.Mask64s, want func(_, _ float64) bool) {
    73  	var z archsimd.Float64s
    74  	testSVECompare(t, float64s, 8, z.Len(), archsimd.LoadFloat64s, cmp, archsimd.Mask64s.Store, want)
    75  }
    76  
    77  func testUint16sCompare(t *testing.T, cmp func(_, _ archsimd.Uint16s) archsimd.Mask16s, want func(_, _ uint16) bool) {
    78  	var z archsimd.Uint16s
    79  	testSVECompare(t, uint16s, 2, z.Len(), archsimd.LoadUint16s, cmp, archsimd.Mask16s.Store, want)
    80  }
    81  
    82  func testUint32sCompare(t *testing.T, cmp func(_, _ archsimd.Uint32s) archsimd.Mask32s, want func(_, _ uint32) bool) {
    83  	var z archsimd.Uint32s
    84  	testSVECompare(t, uint32s, 4, z.Len(), archsimd.LoadUint32s, cmp, archsimd.Mask32s.Store, want)
    85  }
    86  
    87  func testUint64sCompare(t *testing.T, cmp func(_, _ archsimd.Uint64s) archsimd.Mask64s, want func(_, _ uint64) bool) {
    88  	var z archsimd.Uint64s
    89  	testSVECompare(t, uint64s, 8, z.Len(), archsimd.LoadUint64s, cmp, archsimd.Mask64s.Store, want)
    90  }
    91  
    92  func gtWant[T number](a, b T) bool { return a > b }
    93  func geWant[T number](a, b T) bool { return a >= b }
    94  func eqWant[T number](a, b T) bool { return a == b }
    95  func neWant[T number](a, b T) bool { return a != b }
    96  
    97  func TestGreaterSVE(t *testing.T) {
    98  	if !archsimd.ARM64.SVE() {
    99  		t.Skip("no SVE")
   100  	}
   101  	testInt8sCompare(t, archsimd.Int8s.Greater, gtWant[int8])
   102  	testInt16sCompare(t, archsimd.Int16s.Greater, gtWant[int16])
   103  	testInt32sCompare(t, archsimd.Int32s.Greater, gtWant[int32])
   104  	testInt64sCompare(t, archsimd.Int64s.Greater, gtWant[int64])
   105  	testUint8sCompare(t, archsimd.Uint8s.Greater, gtWant[uint8])
   106  	testUint16sCompare(t, archsimd.Uint16s.Greater, gtWant[uint16])
   107  	testUint32sCompare(t, archsimd.Uint32s.Greater, gtWant[uint32])
   108  	testUint64sCompare(t, archsimd.Uint64s.Greater, gtWant[uint64])
   109  	testFloat32sCompare(t, archsimd.Float32s.Greater, gtWant[float32])
   110  	testFloat64sCompare(t, archsimd.Float64s.Greater, gtWant[float64])
   111  }
   112  
   113  // TestMaskStoreLoadPanicSVE checks that the exported mask memory APIs panic when
   114  // the bits slice is too short to hold the whole predicate.
   115  func TestMaskStoreLoadPanicSVE(t *testing.T) {
   116  	if !archsimd.ARM64.SVE() {
   117  		t.Skip("no sve")
   118  	}
   119  	var z archsimd.Int8s
   120  	m := z.Greater(z)
   121  	mustPanic(t, "Store short", func() { m.Store(nil) })
   122  	mustPanic(t, "LoadMask8s short", func() { archsimd.LoadMask8s(nil) })
   123  	// A slice long enough for the whole predicate must not panic.
   124  	bits := make([]uint16, sveMaskUint16s)
   125  	m.Store(bits)
   126  	archsimd.LoadMask8s(bits)
   127  }
   128  
   129  func TestEqualSVE(t *testing.T) {
   130  	if !archsimd.ARM64.SVE() {
   131  		t.Skip("no SVE")
   132  	}
   133  	testInt8sCompare(t, archsimd.Int8s.Equal, eqWant[int8])
   134  	testInt16sCompare(t, archsimd.Int16s.Equal, eqWant[int16])
   135  	testInt32sCompare(t, archsimd.Int32s.Equal, eqWant[int32])
   136  	testInt64sCompare(t, archsimd.Int64s.Equal, eqWant[int64])
   137  	testUint8sCompare(t, archsimd.Uint8s.Equal, eqWant[uint8])
   138  	testUint16sCompare(t, archsimd.Uint16s.Equal, eqWant[uint16])
   139  	testUint32sCompare(t, archsimd.Uint32s.Equal, eqWant[uint32])
   140  	testUint64sCompare(t, archsimd.Uint64s.Equal, eqWant[uint64])
   141  	testFloat32sCompare(t, archsimd.Float32s.Equal, eqWant[float32])
   142  	testFloat64sCompare(t, archsimd.Float64s.Equal, eqWant[float64])
   143  }
   144  
   145  func TestNotEqualSVE(t *testing.T) {
   146  	if !archsimd.ARM64.SVE() {
   147  		t.Skip("no SVE")
   148  	}
   149  	testInt8sCompare(t, archsimd.Int8s.NotEqual, neWant[int8])
   150  	testInt16sCompare(t, archsimd.Int16s.NotEqual, neWant[int16])
   151  	testInt32sCompare(t, archsimd.Int32s.NotEqual, neWant[int32])
   152  	testInt64sCompare(t, archsimd.Int64s.NotEqual, neWant[int64])
   153  	testUint8sCompare(t, archsimd.Uint8s.NotEqual, neWant[uint8])
   154  	testUint16sCompare(t, archsimd.Uint16s.NotEqual, neWant[uint16])
   155  	testUint32sCompare(t, archsimd.Uint32s.NotEqual, neWant[uint32])
   156  	testUint64sCompare(t, archsimd.Uint64s.NotEqual, neWant[uint64])
   157  	testFloat32sCompare(t, archsimd.Float32s.NotEqual, neWant[float32])
   158  	testFloat64sCompare(t, archsimd.Float64s.NotEqual, neWant[float64])
   159  }
   160  
   161  func TestGreaterEqualSVE(t *testing.T) {
   162  	if !archsimd.ARM64.SVE() {
   163  		t.Skip("no SVE")
   164  	}
   165  	testInt8sCompare(t, archsimd.Int8s.GreaterEqual, geWant[int8])
   166  	testInt16sCompare(t, archsimd.Int16s.GreaterEqual, geWant[int16])
   167  	testInt32sCompare(t, archsimd.Int32s.GreaterEqual, geWant[int32])
   168  	testInt64sCompare(t, archsimd.Int64s.GreaterEqual, geWant[int64])
   169  	testUint8sCompare(t, archsimd.Uint8s.GreaterEqual, geWant[uint8])
   170  	testUint16sCompare(t, archsimd.Uint16s.GreaterEqual, geWant[uint16])
   171  	testUint32sCompare(t, archsimd.Uint32s.GreaterEqual, geWant[uint32])
   172  	testUint64sCompare(t, archsimd.Uint64s.GreaterEqual, geWant[uint64])
   173  	testFloat32sCompare(t, archsimd.Float32s.GreaterEqual, geWant[float32])
   174  	testFloat64sCompare(t, archsimd.Float64s.GreaterEqual, geWant[float64])
   175  }
   176  

View as plain text