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  

View as plain text