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

     1  // Copyright 2025 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 && (amd64 || wasm || arm64)
     6  
     7  package simd_test
     8  
     9  import (
    10  	"reflect"
    11  	"simd/archsimd"
    12  	"testing"
    13  )
    14  
    15  var sink any
    16  
    17  func TestType(t *testing.T) {
    18  	// Testing:
    19  	// - Defined as another struct's field is ok
    20  	// - Pointer is ok
    21  	// - Type definition is ok
    22  	// - Type alias is ok
    23  	// - Type conversion is ok
    24  	// - Conversion to interface is ok
    25  	type alias = archsimd.Int32x4
    26  	type vecT archsimd.Int32x4
    27  	type myStruct struct {
    28  		x alias
    29  		y *archsimd.Int32x4
    30  		z vecT
    31  	}
    32  	vals := [4]int32{1, 2, 3, 4}
    33  	v := myStruct{x: archsimd.LoadInt32x4Array(&vals)}
    34  	want := []int32{12, 24, 36, 48}
    35  	y := archsimd.LoadInt32x4Array(&vals)
    36  	v.y = &y
    37  	sink = y
    38  
    39  	v.z = vecT(archsimd.LoadInt32x4Array(&[4]int32{10, 20, 30, 40}))
    40  	*v.y = v.y.Add(v.x).Add(archsimd.Int32x4(v.z))
    41  
    42  	got := [4]int32{}
    43  	v.y.StoreArray(&got)
    44  	checkSlices(t, got[:], want)
    45  }
    46  
    47  func TestUncomparable(t *testing.T) {
    48  	// Test that simd vectors are not comparable
    49  	var x, y any = archsimd.LoadUint32x4Array(&[4]uint32{1, 2, 3, 4}), archsimd.LoadUint32x4Array(&[4]uint32{5, 6, 7, 8})
    50  	shouldPanic := func(fn func()) {
    51  		defer func() {
    52  			if recover() == nil {
    53  				panic("did not panic")
    54  			}
    55  		}()
    56  		fn()
    57  	}
    58  	shouldPanic(func() { _ = x == y })
    59  }
    60  
    61  func TestFuncValue(t *testing.T) {
    62  	// Test that simd intrinsic can be used as a function value.
    63  	xv := [4]int32{1, 2, 3, 4}
    64  	yv := [4]int32{5, 6, 7, 8}
    65  	want := []int32{6, 8, 10, 12}
    66  	x := archsimd.LoadInt32x4Array(&xv)
    67  	y := archsimd.LoadInt32x4Array(&yv)
    68  	fn := archsimd.Int32x4.Add
    69  	sink = fn
    70  	x = fn(x, y)
    71  	got := [4]int32{}
    72  	x.StoreArray(&got)
    73  	checkSlices(t, got[:], want)
    74  }
    75  
    76  func TestReflectMethod(t *testing.T) {
    77  	// Test that simd intrinsic can be accessed via reflection.
    78  	// NOTE: we don't yet support reflect method.Call.
    79  	xv := [4]int32{1, 2, 3, 4}
    80  	yv := [4]int32{5, 6, 7, 8}
    81  	want := []int32{6, 8, 10, 12}
    82  	x := archsimd.LoadInt32x4Array(&xv)
    83  	y := archsimd.LoadInt32x4Array(&yv)
    84  	m, ok := reflect.TypeOf(x).MethodByName("Add")
    85  	if !ok {
    86  		t.Fatal("Add method not found")
    87  	}
    88  	fn := m.Func.Interface().(func(x, y archsimd.Int32x4) archsimd.Int32x4)
    89  	x = fn(x, y)
    90  	got := [4]int32{}
    91  	x.StoreArray(&got)
    92  	checkSlices(t, got[:], want)
    93  }
    94  
    95  func TestVectorConversion(t *testing.T) {
    96  	xv := [4]int32{1, 2, 3, 4}
    97  	x := archsimd.LoadInt32x4Array(&xv)
    98  	xPromoted := x.ToBits().ReshapeToUint64s().BitsToInt64()
    99  	xPromotedDemoted := xPromoted.ToBits().ReshapeToUint32s().BitsToInt32()
   100  	got := [4]int32{}
   101  	xPromotedDemoted.StoreArray(&got)
   102  	for i := range 4 {
   103  		if xv[i] != got[i] {
   104  			t.Errorf("Result at %d incorrect: want %d, got %d", i, xv[i], got[i])
   105  		}
   106  	}
   107  }
   108  
   109  func TestMaskConversion(t *testing.T) {
   110  	x := archsimd.LoadInt32x4([]int32{5, 0, 7, 0})
   111  	mask := archsimd.Int32x4{}.Sub(x).ToMask()
   112  	y := archsimd.LoadInt32x4([]int32{1, 2, 3, 4}).Add(x).Masked(mask)
   113  	want := [4]int32{6, 0, 10, 0}
   114  	got := make([]int32, 4)
   115  	y.Store(got)
   116  	checkSlices(t, got[:], want[:])
   117  }
   118  
   119  func TestBroadcastUint32x4(t *testing.T) {
   120  	s := make([]uint32, 4, 4)
   121  	archsimd.BroadcastUint32x4(123456789).Store(s)
   122  	checkSlices(t, s, []uint32{123456789, 123456789, 123456789, 123456789})
   123  }
   124  
   125  func TestBroadcastFloat32x4(t *testing.T) {
   126  	s := make([]float32, 4, 4)
   127  	archsimd.BroadcastFloat32x4(3.14).Store(s)
   128  	checkSlices(t, s, []float32{3.14, 3.14, 3.14, 3.14})
   129  }
   130  
   131  func TestBroadcastFloat64x2(t *testing.T) {
   132  	s := make([]float64, 2, 2)
   133  	archsimd.BroadcastFloat64x2(3.14).Store(s)
   134  	checkSlices(t, s, []float64{3.14, 3.14})
   135  }
   136  
   137  func TestBroadcastUint64x2(t *testing.T) {
   138  	s := make([]uint64, 2, 2)
   139  	archsimd.BroadcastUint64x2(123456789012345).Store(s)
   140  	checkSlices(t, s, []uint64{123456789012345, 123456789012345})
   141  }
   142  
   143  func TestBroadcastUint16x8(t *testing.T) {
   144  	s := make([]uint16, 8, 8)
   145  	archsimd.BroadcastUint16x8(12345).Store(s)
   146  	checkSlices(t, s, []uint16{12345, 12345, 12345, 12345, 12345, 12345, 12345, 12345})
   147  }
   148  
   149  func TestBroadcastInt8x16(t *testing.T) {
   150  	s := make([]int8, 16, 16)
   151  	archsimd.BroadcastInt8x16(-123).Store(s)
   152  	checkSlices(t, s, []int8{-123, -123, -123, -123, -123, -123, -123, -123,
   153  		-123, -123, -123, -123, -123, -123, -123, -123})
   154  }
   155  
   156  func TestBroadcastUint8x16(t *testing.T) {
   157  	s := make([]uint8, 16, 16)
   158  	archsimd.BroadcastUint8x16(200).Store(s)
   159  	checkSlices(t, s, []uint8{200, 200, 200, 200, 200, 200, 200, 200,
   160  		200, 200, 200, 200, 200, 200, 200, 200})
   161  }
   162  
   163  func TestBroadcastInt16x8(t *testing.T) {
   164  	s := make([]int16, 8, 8)
   165  	archsimd.BroadcastInt16x8(-12345).Store(s)
   166  	checkSlices(t, s, []int16{-12345, -12345, -12345, -12345, -12345, -12345, -12345, -12345})
   167  }
   168  
   169  func TestBroadcastInt32x4(t *testing.T) {
   170  	s := make([]int32, 4, 4)
   171  	archsimd.BroadcastInt32x4(-123456789).Store(s)
   172  	checkSlices(t, s, []int32{-123456789, -123456789, -123456789, -123456789})
   173  }
   174  
   175  func TestBroadcastInt64x2(t *testing.T) {
   176  	s := make([]int64, 2, 2)
   177  	archsimd.BroadcastInt64x2(-123456789).Store(s)
   178  	checkSlices(t, s, []int64{-123456789, -123456789})
   179  }
   180  
   181  func TestString(t *testing.T) {
   182  	x := archsimd.LoadUint32x4([]uint32{0, 1, 2, 3})
   183  	y := archsimd.LoadInt64x2([]int64{-44, -5})
   184  	z := archsimd.LoadFloat32x4([]float32{0.5, 1.5, -2.5, 3.5e9})
   185  	w := archsimd.LoadFloat64x2([]float64{-2.5, 3.5e9})
   186  
   187  	sx := "{0,1,2,3}"
   188  	sy := "{-44,-5}"
   189  	sz := "{0.5,1.5,-2.5,3.5e+09}"
   190  	sw := "{-2.5,3.5e+09}"
   191  
   192  	if x.String() != sx {
   193  		t.Errorf("x=%s wanted %s", x, sx)
   194  	}
   195  	if y.String() != sy {
   196  		t.Errorf("y=%s wanted %s", y, sy)
   197  	}
   198  	if z.String() != sz {
   199  		t.Errorf("z=%s wanted %s", z, sz)
   200  	}
   201  	if w.String() != sw {
   202  		t.Errorf("w=%s wanted %s", w, sw)
   203  	}
   204  	t.Logf("w=%s", w)
   205  	t.Logf("x=%s", x)
   206  	t.Logf("y=%s", y)
   207  	t.Logf("z=%s", z)
   208  }
   209  
   210  func stringy[T interface{ String() string }](v T) string {
   211  	return v.String()
   212  }
   213  
   214  func double[T interface{ Add(T) T }](v T) T {
   215  	return v.Add(v)
   216  }
   217  
   218  // Test that vector type instantiation works correctly, see issue #77444.
   219  func TestTypeParam(t *testing.T) {
   220  	x := archsimd.LoadInt64x2([]int64{1, 1})
   221  	y := archsimd.LoadInt64x2([]int64{1, 1})
   222  	if got := stringy(x); got != y.String() {
   223  		t.Fatalf("string(x) = %q, want %q", got, y.String())
   224  	}
   225  	var want, got [2]int64
   226  	y.Add(y).StoreArray(&want)
   227  	double(x).StoreArray(&got)
   228  	if got != want {
   229  		t.Fatalf("double(x) = %v, want %v", got, want)
   230  	}
   231  }
   232  
   233  func TestManyFloats(t *testing.T) {
   234  	// This test doesn't do anything SIMD, just test that we can
   235  	// handle correctly a large number of floating point values,
   236  	// as floating point uses same registers as SIMD, but the SSE
   237  	// instructions can only work on low-numbered ones.
   238  	testManyFloats(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
   239  		17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32)
   240  }
   241  
   242  var float64Sink float64
   243  
   244  //go:noinline
   245  func testManyFloats(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16,
   246  	a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32 float64) {
   247  	float64Sink += a1
   248  	float64Sink *= a2
   249  	float64Sink -= a3
   250  	float64Sink /= a4
   251  	float64Sink += a5
   252  	float64Sink *= a6
   253  	float64Sink -= a7
   254  	float64Sink /= a8
   255  	float64Sink += a9
   256  	float64Sink *= a10
   257  	float64Sink -= a11
   258  	float64Sink /= a12
   259  	float64Sink += a13
   260  	float64Sink *= a14
   261  	float64Sink -= a15
   262  	float64Sink /= a16
   263  	float64Sink += a17
   264  	float64Sink *= a18
   265  	float64Sink -= a19
   266  	float64Sink /= a20
   267  	float64Sink += a21
   268  	float64Sink *= a22
   269  	float64Sink -= a23
   270  	float64Sink /= a24
   271  	float64Sink += a25
   272  	float64Sink *= a26
   273  	float64Sink -= a27
   274  	float64Sink /= a28
   275  	float64Sink += a29
   276  	float64Sink *= a30
   277  	float64Sink -= a31
   278  	float64Sink /= a32
   279  }
   280  
   281  func TestSlicesInt8SetElem(t *testing.T) {
   282  	a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
   283  		17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}
   284  	v := archsimd.LoadInt8x16(a)
   285  
   286  	v = v.SetElem(3, 13)
   287  	a[3] = 13
   288  
   289  	b := make([]int8, 16, 16)
   290  	v.Store(b)
   291  	checkSlices(t, a, b)
   292  }
   293  
   294  func TestSlicesInt8GetElem(t *testing.T) {
   295  	a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
   296  		17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}
   297  	v := archsimd.LoadInt8x16(a)
   298  	e := v.GetElem(2)
   299  	if e != a[2] {
   300  		t.Errorf("GetElem(2) = %d != a[2] = %d", e, a[2])
   301  	}
   302  
   303  }
   304  
   305  var seventeen = uint8(17)
   306  
   307  func TestSlicesInt8GetElem16(t *testing.T) {
   308  	defer func() {
   309  		if r := recover(); r != nil {
   310  			t.Logf("Saw EXPECTED panic %v", r)
   311  		} else {
   312  			t.Errorf("Did not see expected panic")
   313  		}
   314  	}()
   315  	a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
   316  	v := archsimd.LoadInt8x16(a)
   317  	e := v.GetElem(seventeen - 1)
   318  	t.Errorf("Should have panicked, e=%v", e)
   319  }
   320  
   321  func TestSlicesInt8GetElem16const(t *testing.T) {
   322  	defer func() {
   323  		if r := recover(); r != nil {
   324  			t.Logf("Saw EXPECTED panic %v", r)
   325  		} else {
   326  			t.Errorf("Did not see expected panic")
   327  		}
   328  	}()
   329  	a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
   330  	v := archsimd.LoadInt8x16(a)
   331  	e := v.GetElem(16)
   332  	t.Errorf("Should have panicked, e=%v", e)
   333  }
   334  
   335  func TestSlicesInt8GetElem15(t *testing.T) {
   336  	a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
   337  	v := archsimd.LoadInt8x16(a)
   338  	e := v.GetElem(seventeen - 2)
   339  	if e != a[15] {
   340  		t.Errorf("GetElem(15) = %d != a[15] = %d", e, a[15])
   341  	}
   342  }
   343  
   344  func TestSlicesInt8GetElem15const(t *testing.T) {
   345  	a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
   346  	v := archsimd.LoadInt8x16(a)
   347  	e := v.GetElem(15)
   348  	if e != a[15] {
   349  		t.Errorf("GetElem(15) = %d != a[15] = %d", e, a[15])
   350  	}
   351  }
   352  
   353  func TestSlicesInt8SetElem17(t *testing.T) {
   354  	defer func() {
   355  		if r := recover(); r != nil {
   356  			t.Logf("Saw EXPECTED panic %v", r)
   357  		} else {
   358  			t.Errorf("Did not see expected panic")
   359  		}
   360  	}()
   361  	a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
   362  	v := archsimd.LoadInt8x16(a)
   363  	e := v.SetElem(seventeen, 18).GetElem(2)
   364  	t.Errorf("Should have panicked, e=%v", e)
   365  }
   366  
   367  func TestSlicesInt8SetElem17const(t *testing.T) {
   368  	defer func() {
   369  		if r := recover(); r != nil {
   370  			t.Logf("Saw EXPECTED panic %v", r)
   371  		} else {
   372  			t.Errorf("Did not see expected panic")
   373  		}
   374  	}()
   375  	a := []int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
   376  	v := archsimd.LoadInt8x16(a)
   377  	e := v.SetElem(17, 18).GetElem(2)
   378  	t.Errorf("Should have panicked, e=%v", e)
   379  }
   380  

View as plain text