Source file src/simd/simd_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
     6  
     7  package simd_test
     8  
     9  import (
    10  	"simd"
    11  	"slices"
    12  	"testing"
    13  )
    14  
    15  type signed interface {
    16  	~int | ~int8 | ~int16 | ~int32 | ~int64
    17  }
    18  
    19  type unsigned interface {
    20  	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
    21  }
    22  
    23  type integer interface {
    24  	~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
    25  }
    26  
    27  type float interface {
    28  	~float32 | ~float64
    29  }
    30  
    31  type number interface {
    32  	~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64
    33  }
    34  
    35  func TestInt8s(t *testing.T) {
    36  	// 64 elements = 512 bits
    37  	in1 := []int8{
    38  		1, -2, 3, -4, 5, -6, 7, -8, 9, -10, 11, -12, 13, -14, 15, -16,
    39  		17, -18, 19, -20, 21, -22, 23, -24, 25, -26, 27, -28, 29, -30, 31, -32,
    40  		33, -34, 35, -36, 37, -38, 39, -40, 41, -42, 43, -44, 45, -46, 47, -48,
    41  		49, -50, 51, -52, 53, -54, 55, -56, 57, -58, 59, -60, 61, -62, 63, -64,
    42  	}
    43  	in2 := make([]int8, 64)
    44  	for i := range in2 {
    45  		in2[i] = 2
    46  	}
    47  
    48  	x := simd.LoadInt8s(in1)
    49  	y := simd.LoadInt8s(in2)
    50  
    51  	if x.Len() <= 0 {
    52  		t.Errorf("Int8s.Len() returned <= 0")
    53  	}
    54  
    55  	sum := x.Add(y)
    56  	diff := x.Sub(y)
    57  	neg := x.Neg()
    58  	abs := x.Abs()
    59  
    60  	buf := make([]int8, x.Len())
    61  	sum.Store(buf)
    62  	for i := 0; i < x.Len() && i < len(in1); i++ {
    63  		expected := in1[i] + in2[i]
    64  		if buf[i] != expected {
    65  			t.Errorf("Add at %d: got %d, want %d", i, buf[i], expected)
    66  		}
    67  	}
    68  
    69  	diff.Store(buf)
    70  	for i := 0; i < x.Len() && i < len(in1); i++ {
    71  		expected := in1[i] - in2[i]
    72  		if buf[i] != expected {
    73  			t.Errorf("Sub at %d: got %d, want %d", i, buf[i], expected)
    74  		}
    75  	}
    76  
    77  	neg.Store(buf)
    78  	for i := 0; i < x.Len() && i < len(in1); i++ {
    79  		expected := -in1[i]
    80  		if buf[i] != expected {
    81  			t.Errorf("Neg at %d: got %d, want %d", i, buf[i], expected)
    82  		}
    83  	}
    84  
    85  	abs.Store(buf)
    86  	for i := 0; i < x.Len() && i < len(in1); i++ {
    87  		expected := in1[i]
    88  		if expected < 0 {
    89  			expected = -expected
    90  		}
    91  		if buf[i] != expected {
    92  			t.Errorf("Abs at %d: got %d, want %d", i, buf[i], expected)
    93  		}
    94  	}
    95  }
    96  
    97  func TestInt16s(t *testing.T) {
    98  	// 32 elements = 512 bits
    99  	in1 := make([]int16, 32)
   100  	in2 := make([]int16, 32)
   101  	for i := range in1 {
   102  		in1[i] = int16((i + 1) * 100)
   103  		if i%2 != 0 {
   104  			in1[i] = -in1[i]
   105  		}
   106  		in2[i] = 10
   107  	}
   108  
   109  	x := simd.LoadInt16s(in1)
   110  	y := simd.LoadInt16s(in2)
   111  
   112  	sum := x.Add(y)
   113  	buf := make([]int16, x.Len())
   114  	sum.Store(buf)
   115  
   116  	for i := 0; i < x.Len() && i < len(in1); i++ {
   117  		expected := in1[i] + in2[i]
   118  		if buf[i] != expected {
   119  			t.Errorf("Int16s Add at %d: got %d, want %d", i, buf[i], expected)
   120  		}
   121  	}
   122  
   123  	// Test RotateAllLeft
   124  	rotLeft := x.RotateAllLeft(3)
   125  	rotLeft.Store(buf)
   126  	for i := 0; i < x.Len() && i < len(in1); i++ {
   127  		val := uint16(in1[i])
   128  		expected := int16((val << 3) | (val >> 13))
   129  		if buf[i] != expected {
   130  			t.Errorf("Int16s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
   131  		}
   132  	}
   133  
   134  	// Test RotateAllRight with large distance
   135  	rotRight := x.RotateAllRight(19)
   136  	rotRight.Store(buf)
   137  	for i := 0; i < x.Len() && i < len(in1); i++ {
   138  		val := uint16(in1[i])
   139  		expected := int16((val >> 3) | (val << 13))
   140  		if buf[i] != expected {
   141  			t.Errorf("Int16s RotateAllRight(19) at %d: got %d, want %d", i, buf[i], expected)
   142  		}
   143  	}
   144  }
   145  
   146  func TestInt32s(t *testing.T) {
   147  	// 16 elements = 512 bits
   148  	in1 := make([]int32, 16)
   149  	in2 := make([]int32, 16)
   150  	for i := range in1 {
   151  		in1[i] = int32((i + 1) * 1000)
   152  		if i%2 != 0 {
   153  			in1[i] = -in1[i]
   154  		}
   155  		in2[i] = 100
   156  	}
   157  
   158  	x := simd.LoadInt32s(in1)
   159  	y := simd.LoadInt32s(in2)
   160  
   161  	sum := x.Add(y)
   162  	buf := make([]int32, x.Len())
   163  	sum.Store(buf)
   164  
   165  	for i := 0; i < x.Len() && i < len(in1); i++ {
   166  		expected := in1[i] + in2[i]
   167  		if buf[i] != expected {
   168  			t.Errorf("Int32s Add at %d: got %d, want %d", i, buf[i], expected)
   169  		}
   170  	}
   171  
   172  	// Test RotateAllLeft
   173  	rotLeft := x.RotateAllLeft(5)
   174  	rotLeft.Store(buf)
   175  	for i := 0; i < x.Len() && i < len(in1); i++ {
   176  		val := uint32(in1[i])
   177  		expected := int32((val << 5) | (val >> 27))
   178  		if buf[i] != expected {
   179  			t.Errorf("Int32s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
   180  		}
   181  	}
   182  
   183  	// Test RotateAllRight with large distance
   184  	rotRight := x.RotateAllRight(37)
   185  	rotRight.Store(buf)
   186  	for i := 0; i < x.Len() && i < len(in1); i++ {
   187  		val := uint32(in1[i])
   188  		expected := int32((val >> 5) | (val << 27))
   189  		if buf[i] != expected {
   190  			t.Errorf("Int32s RotateAllRight(37) at %d: got %d, want %d", i, buf[i], expected)
   191  		}
   192  	}
   193  }
   194  
   195  func TestInt64s(t *testing.T) {
   196  	// 8 elements = 512 bits
   197  	in1 := make([]int64, 8)
   198  	in2 := make([]int64, 8)
   199  	for i := range in1 {
   200  		in1[i] = int64((i + 1) * 10000)
   201  		if i%2 != 0 {
   202  			in1[i] = -in1[i]
   203  		}
   204  		in2[i] = 1000
   205  	}
   206  
   207  	x := simd.LoadInt64s(in1)
   208  	y := simd.LoadInt64s(in2)
   209  
   210  	sum := x.Add(y)
   211  	buf := make([]int64, x.Len())
   212  	sum.Store(buf)
   213  
   214  	for i := 0; i < x.Len() && i < len(in1); i++ {
   215  		expected := in1[i] + in2[i]
   216  		if buf[i] != expected {
   217  			t.Errorf("Int64s Add at %d: got %d, want %d", i, buf[i], expected)
   218  		}
   219  	}
   220  
   221  	// Test RotateAllLeft
   222  	rotLeft := x.RotateAllLeft(7)
   223  	rotLeft.Store(buf)
   224  	for i := 0; i < x.Len() && i < len(in1); i++ {
   225  		val := uint64(in1[i])
   226  		expected := int64((val << 7) | (val >> 57))
   227  		if buf[i] != expected {
   228  			t.Errorf("Int64s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
   229  		}
   230  	}
   231  
   232  	// Test RotateAllRight with large distance
   233  	rotRight := x.RotateAllRight(71)
   234  	rotRight.Store(buf)
   235  	for i := 0; i < x.Len() && i < len(in1); i++ {
   236  		val := uint64(in1[i])
   237  		expected := int64((val >> 7) | (val << 57))
   238  		if buf[i] != expected {
   239  			t.Errorf("Int64s RotateAllRight(71) at %d: got %d, want %d", i, buf[i], expected)
   240  		}
   241  	}
   242  }
   243  
   244  func TestUint8s(t *testing.T) {
   245  	// 64 elements = 512 bits
   246  	in1 := make([]uint8, 64)
   247  	in2 := make([]uint8, 64)
   248  	for i := range in1 {
   249  		in1[i] = uint8(i + 1)
   250  		in2[i] = 10
   251  	}
   252  
   253  	x := simd.LoadUint8s(in1)
   254  	y := simd.LoadUint8s(in2)
   255  
   256  	avg := x.Average(y)
   257  	buf := make([]uint8, x.Len())
   258  	avg.Store(buf)
   259  
   260  	for i := 0; i < x.Len() && i < len(in1); i++ {
   261  		expected := uint8((int(in1[i]) + int(in2[i]) + 1) >> 1)
   262  		if buf[i] != expected {
   263  			t.Errorf("Uint8s Average at %d: got %d, want %d", i, buf[i], expected)
   264  		}
   265  	}
   266  }
   267  
   268  func TestFloat32s(t *testing.T) {
   269  	// 16 elements = 512 bits
   270  	in1 := make([]float32, 16)
   271  	in2 := make([]float32, 16)
   272  	for i := range in1 {
   273  		val := float32(i) + 1.5
   274  		if i%2 != 0 {
   275  			val = -val
   276  		}
   277  		in1[i] = val
   278  		in2[i] = 0.5
   279  	}
   280  
   281  	x := simd.LoadFloat32s(in1)
   282  	y := simd.LoadFloat32s(in2)
   283  
   284  	sum := x.Add(y)
   285  	buf := make([]float32, x.Len())
   286  	sum.Store(buf)
   287  
   288  	for i := 0; i < x.Len() && i < len(in1); i++ {
   289  		expected := in1[i] + in2[i]
   290  		if buf[i] != expected {
   291  			t.Errorf("Float32s Add at %d: got %f, want %f", i, buf[i], expected)
   292  		}
   293  	}
   294  }
   295  
   296  func TestFloat64s(t *testing.T) {
   297  	// 8 elements = 512 bits
   298  	in1 := make([]float64, 8)
   299  	in2 := make([]float64, 8)
   300  	for i := range in1 {
   301  		val := float64(i)*10.0 + 10.25
   302  		if i%2 != 0 {
   303  			val = -val
   304  		}
   305  		in1[i] = val
   306  		in2[i] = 1.0
   307  	}
   308  
   309  	x := simd.LoadFloat64s(in1)
   310  	y := simd.LoadFloat64s(in2)
   311  
   312  	mul := x.Mul(y)
   313  	buf := make([]float64, x.Len())
   314  	mul.Store(buf)
   315  
   316  	for i := 0; i < x.Len() && i < len(in1); i++ {
   317  		expected := in1[i] * in2[i]
   318  		if buf[i] != expected {
   319  			t.Errorf("Float64s Mul at %d: got %f, want %f", i, buf[i], expected)
   320  		}
   321  	}
   322  }
   323  
   324  func TestUint16s(t *testing.T) {
   325  	in1 := make([]uint16, 32)
   326  	for i := range in1 {
   327  		in1[i] = uint16((i + 1) * 100)
   328  	}
   329  
   330  	x := simd.LoadUint16s(in1)
   331  	buf := make([]uint16, x.Len())
   332  
   333  	// Test RotateAllLeft
   334  	rotLeft := x.RotateAllLeft(3)
   335  	rotLeft.Store(buf)
   336  	for i := 0; i < x.Len() && i < len(in1); i++ {
   337  		val := in1[i]
   338  		expected := (val << 3) | (val >> 13)
   339  		if buf[i] != expected {
   340  			t.Errorf("Uint16s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
   341  		}
   342  	}
   343  
   344  	// Test RotateAllRight with large distance
   345  	rotRight := x.RotateAllRight(19)
   346  	rotRight.Store(buf)
   347  	for i := 0; i < x.Len() && i < len(in1); i++ {
   348  		val := in1[i]
   349  		expected := (val >> 3) | (val << 13)
   350  		if buf[i] != expected {
   351  			t.Errorf("Uint16s RotateAllRight(19) at %d: got %d, want %d", i, buf[i], expected)
   352  		}
   353  	}
   354  }
   355  
   356  func TestUint32s(t *testing.T) {
   357  	in1 := make([]uint32, 16)
   358  	for i := range in1 {
   359  		in1[i] = uint32((i + 1) * 1000)
   360  	}
   361  
   362  	x := simd.LoadUint32s(in1)
   363  	buf := make([]uint32, x.Len())
   364  
   365  	// Test RotateAllLeft
   366  	rotLeft := x.RotateAllLeft(5)
   367  	rotLeft.Store(buf)
   368  	for i := 0; i < x.Len() && i < len(in1); i++ {
   369  		val := in1[i]
   370  		expected := (val << 5) | (val >> 27)
   371  		if buf[i] != expected {
   372  			t.Errorf("Uint32s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
   373  		}
   374  	}
   375  
   376  	// Test RotateAllRight with large distance
   377  	rotRight := x.RotateAllRight(37)
   378  	rotRight.Store(buf)
   379  	for i := 0; i < x.Len() && i < len(in1); i++ {
   380  		val := in1[i]
   381  		expected := (val >> 5) | (val << 27)
   382  		if buf[i] != expected {
   383  			t.Errorf("Uint32s RotateAllRight(37) at %d: got %d, want %d", i, buf[i], expected)
   384  		}
   385  	}
   386  }
   387  
   388  func TestUint64s(t *testing.T) {
   389  	in1 := make([]uint64, 8)
   390  	for i := range in1 {
   391  		in1[i] = uint64((i + 1) * 10000)
   392  	}
   393  
   394  	x := simd.LoadUint64s(in1)
   395  	buf := make([]uint64, x.Len())
   396  
   397  	// Test RotateAllLeft
   398  	rotLeft := x.RotateAllLeft(7)
   399  	rotLeft.Store(buf)
   400  	for i := 0; i < x.Len() && i < len(in1); i++ {
   401  		val := in1[i]
   402  		expected := (val << 7) | (val >> 57)
   403  		if buf[i] != expected {
   404  			t.Errorf("Uint64s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
   405  		}
   406  	}
   407  
   408  	// Test RotateAllRight with large distance
   409  	rotRight := x.RotateAllRight(71)
   410  	rotRight.Store(buf)
   411  	for i := 0; i < x.Len() && i < len(in1); i++ {
   412  		val := in1[i]
   413  		expected := (val >> 7) | (val << 57)
   414  		if buf[i] != expected {
   415  			t.Errorf("Uint64s RotateAllRight(71) at %d: got %d, want %d", i, buf[i], expected)
   416  		}
   417  	}
   418  }
   419  
   420  type HasStoreLen[E number] interface {
   421  	Store(s []E)
   422  	Len() int
   423  }
   424  
   425  func testBroadcast[E number, V HasStoreLen[E]](t *testing.T, x E, f func(e E) V) {
   426  	v := f(x)
   427  	s := make([]E, v.Len())
   428  	v.Store(s)
   429  	for _, e := range s {
   430  		if e != x {
   431  			t.Errorf("Expected %v, saw %v", x, e)
   432  		}
   433  	}
   434  }
   435  
   436  func TestBroadcast(t *testing.T) {
   437  	testBroadcast(t, int8(-2), simd.BroadcastInt8s)
   438  	testBroadcast(t, int16(-2), simd.BroadcastInt16s)
   439  	testBroadcast(t, int32(-2), simd.BroadcastInt32s)
   440  	testBroadcast(t, int64(-2), simd.BroadcastInt64s)
   441  
   442  	testBroadcast(t, uint8(99), simd.BroadcastUint8s)
   443  	testBroadcast(t, uint16(9999), simd.BroadcastUint16s)
   444  	testBroadcast(t, uint32(99991111), simd.BroadcastUint32s)
   445  	testBroadcast(t, uint64(112233445599887766), simd.BroadcastUint64s)
   446  
   447  	testBroadcast(t, float32(99991111), simd.BroadcastFloat32s)
   448  	testBroadcast(t, float64(112233445599887766), simd.BroadcastFloat64s)
   449  }
   450  
   451  func TestMaskToInt(t *testing.T) {
   452  	topBits := simd.BroadcastUint8s(0x80)
   453  	got := make([]int8, topBits.Len())
   454  	topBits.Equal(topBits).ToInt8s().Store(got)
   455  	want := slices.Repeat([]int8{-1}, topBits.Len())
   456  	if !slices.Equal(want, got) {
   457  		t.Errorf("Wanted %v, got %v", want, got)
   458  	}
   459  }
   460  
   461  //go:noinline
   462  func four() uint64 {
   463  	return 4
   464  }
   465  
   466  func TestShiftAllLeft(t *testing.T) {
   467  	// Int16s on 512-bit vector has 32 elements.
   468  	in := []int16{
   469  		1, 2, 4, 8, 16, 32, 64, 128,
   470  		0, 0, 0, 0, 0, 0, 0, 0,
   471  		0, 0, 0, 0, 0, 0, 0, 0,
   472  		0, 0, 0, 0, 0, 0, 0, 0,
   473  	}
   474  
   475  	v := simd.LoadInt16s(in)
   476  	want := []int16{
   477  		1 << 4, 2 << 4, 4 << 4, 8 << 4, 16 << 4, 32 << 4, 64 << 4, 128 << 4,
   478  		0, 0, 0, 0, 0, 0, 0, 0,
   479  		0, 0, 0, 0, 0, 0, 0, 0,
   480  		0, 0, 0, 0, 0, 0, 0, 0,
   481  	}[:v.Len()]
   482  
   483  	{
   484  		// Shift all elements left by 4 bits.
   485  		res := v.ShiftAllLeft(4)
   486  		got := make([]int16, res.Len())
   487  		res.Store(got)
   488  		if !slices.Equal(want, got) {
   489  			t.Errorf("Wanted %v, got %v", want, got)
   490  		}
   491  	}
   492  	{
   493  		// Shift all elements left by 4 bits.
   494  		res := v.ShiftAllLeft(four())
   495  		got := make([]int16, res.Len())
   496  		res.Store(got)
   497  		if !slices.Equal(want, got) {
   498  			t.Errorf("Wanted %v, got %v", want, got)
   499  		}
   500  	}
   501  }
   502  

View as plain text