blob: 099c52e7ee7169704b32f9f2e69db3bb648f4b0f [file]
// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build goexperiment.simd
package simd_test
import (
"simd"
"testing"
)
type signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
type unsigned interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
type integer interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
type float interface {
~float32 | ~float64
}
type number interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64
}
func TestInt8s(t *testing.T) {
// 64 elements = 512 bits
in1 := []int8{
1, -2, 3, -4, 5, -6, 7, -8, 9, -10, 11, -12, 13, -14, 15, -16,
17, -18, 19, -20, 21, -22, 23, -24, 25, -26, 27, -28, 29, -30, 31, -32,
33, -34, 35, -36, 37, -38, 39, -40, 41, -42, 43, -44, 45, -46, 47, -48,
49, -50, 51, -52, 53, -54, 55, -56, 57, -58, 59, -60, 61, -62, 63, -64,
}
in2 := make([]int8, 64)
for i := range in2 {
in2[i] = 2
}
x := simd.LoadInt8s(in1)
y := simd.LoadInt8s(in2)
if x.Len() <= 0 {
t.Errorf("Int8s.Len() returned <= 0")
}
sum := x.Add(y)
diff := x.Sub(y)
neg := x.Neg()
abs := x.Abs()
buf := make([]int8, x.Len())
sum.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
expected := in1[i] + in2[i]
if buf[i] != expected {
t.Errorf("Add at %d: got %d, want %d", i, buf[i], expected)
}
}
diff.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
expected := in1[i] - in2[i]
if buf[i] != expected {
t.Errorf("Sub at %d: got %d, want %d", i, buf[i], expected)
}
}
neg.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
expected := -in1[i]
if buf[i] != expected {
t.Errorf("Neg at %d: got %d, want %d", i, buf[i], expected)
}
}
abs.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
expected := in1[i]
if expected < 0 {
expected = -expected
}
if buf[i] != expected {
t.Errorf("Abs at %d: got %d, want %d", i, buf[i], expected)
}
}
}
func TestInt16s(t *testing.T) {
// 32 elements = 512 bits
in1 := make([]int16, 32)
in2 := make([]int16, 32)
for i := range in1 {
in1[i] = int16((i + 1) * 100)
if i%2 != 0 {
in1[i] = -in1[i]
}
in2[i] = 10
}
x := simd.LoadInt16s(in1)
y := simd.LoadInt16s(in2)
sum := x.Add(y)
buf := make([]int16, x.Len())
sum.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
expected := in1[i] + in2[i]
if buf[i] != expected {
t.Errorf("Int16s Add at %d: got %d, want %d", i, buf[i], expected)
}
}
// Test RotateAllLeft
rotLeft := x.RotateAllLeft(3)
rotLeft.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := uint16(in1[i])
expected := int16((val << 3) | (val >> 13))
if buf[i] != expected {
t.Errorf("Int16s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
}
}
// Test RotateAllRight with large distance
rotRight := x.RotateAllRight(19)
rotRight.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := uint16(in1[i])
expected := int16((val >> 3) | (val << 13))
if buf[i] != expected {
t.Errorf("Int16s RotateAllRight(19) at %d: got %d, want %d", i, buf[i], expected)
}
}
}
func TestInt32s(t *testing.T) {
// 16 elements = 512 bits
in1 := make([]int32, 16)
in2 := make([]int32, 16)
for i := range in1 {
in1[i] = int32((i + 1) * 1000)
if i%2 != 0 {
in1[i] = -in1[i]
}
in2[i] = 100
}
x := simd.LoadInt32s(in1)
y := simd.LoadInt32s(in2)
sum := x.Add(y)
buf := make([]int32, x.Len())
sum.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
expected := in1[i] + in2[i]
if buf[i] != expected {
t.Errorf("Int32s Add at %d: got %d, want %d", i, buf[i], expected)
}
}
// Test RotateAllLeft
rotLeft := x.RotateAllLeft(5)
rotLeft.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := uint32(in1[i])
expected := int32((val << 5) | (val >> 27))
if buf[i] != expected {
t.Errorf("Int32s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
}
}
// Test RotateAllRight with large distance
rotRight := x.RotateAllRight(37)
rotRight.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := uint32(in1[i])
expected := int32((val >> 5) | (val << 27))
if buf[i] != expected {
t.Errorf("Int32s RotateAllRight(37) at %d: got %d, want %d", i, buf[i], expected)
}
}
}
func TestInt64s(t *testing.T) {
// 8 elements = 512 bits
in1 := make([]int64, 8)
in2 := make([]int64, 8)
for i := range in1 {
in1[i] = int64((i + 1) * 10000)
if i%2 != 0 {
in1[i] = -in1[i]
}
in2[i] = 1000
}
x := simd.LoadInt64s(in1)
y := simd.LoadInt64s(in2)
sum := x.Add(y)
buf := make([]int64, x.Len())
sum.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
expected := in1[i] + in2[i]
if buf[i] != expected {
t.Errorf("Int64s Add at %d: got %d, want %d", i, buf[i], expected)
}
}
// Test RotateAllLeft
rotLeft := x.RotateAllLeft(7)
rotLeft.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := uint64(in1[i])
expected := int64((val << 7) | (val >> 57))
if buf[i] != expected {
t.Errorf("Int64s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
}
}
// Test RotateAllRight with large distance
rotRight := x.RotateAllRight(71)
rotRight.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := uint64(in1[i])
expected := int64((val >> 7) | (val << 57))
if buf[i] != expected {
t.Errorf("Int64s RotateAllRight(71) at %d: got %d, want %d", i, buf[i], expected)
}
}
}
func TestUint8s(t *testing.T) {
// 64 elements = 512 bits
in1 := make([]uint8, 64)
in2 := make([]uint8, 64)
for i := range in1 {
in1[i] = uint8(i + 1)
in2[i] = 10
}
x := simd.LoadUint8s(in1)
y := simd.LoadUint8s(in2)
avg := x.Average(y)
buf := make([]uint8, x.Len())
avg.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
expected := uint8((int(in1[i]) + int(in2[i]) + 1) >> 1)
if buf[i] != expected {
t.Errorf("Uint8s Average at %d: got %d, want %d", i, buf[i], expected)
}
}
}
func TestFloat32s(t *testing.T) {
// 16 elements = 512 bits
in1 := make([]float32, 16)
in2 := make([]float32, 16)
for i := range in1 {
val := float32(i) + 1.5
if i%2 != 0 {
val = -val
}
in1[i] = val
in2[i] = 0.5
}
x := simd.LoadFloat32s(in1)
y := simd.LoadFloat32s(in2)
sum := x.Add(y)
buf := make([]float32, x.Len())
sum.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
expected := in1[i] + in2[i]
if buf[i] != expected {
t.Errorf("Float32s Add at %d: got %f, want %f", i, buf[i], expected)
}
}
}
func TestFloat64s(t *testing.T) {
// 8 elements = 512 bits
in1 := make([]float64, 8)
in2 := make([]float64, 8)
for i := range in1 {
val := float64(i)*10.0 + 10.25
if i%2 != 0 {
val = -val
}
in1[i] = val
in2[i] = 1.0
}
x := simd.LoadFloat64s(in1)
y := simd.LoadFloat64s(in2)
mul := x.Mul(y)
buf := make([]float64, x.Len())
mul.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
expected := in1[i] * in2[i]
if buf[i] != expected {
t.Errorf("Float64s Mul at %d: got %f, want %f", i, buf[i], expected)
}
}
}
func TestUint16s(t *testing.T) {
in1 := make([]uint16, 32)
for i := range in1 {
in1[i] = uint16((i + 1) * 100)
}
x := simd.LoadUint16s(in1)
buf := make([]uint16, x.Len())
// Test RotateAllLeft
rotLeft := x.RotateAllLeft(3)
rotLeft.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := in1[i]
expected := (val << 3) | (val >> 13)
if buf[i] != expected {
t.Errorf("Uint16s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
}
}
// Test RotateAllRight with large distance
rotRight := x.RotateAllRight(19)
rotRight.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := in1[i]
expected := (val >> 3) | (val << 13)
if buf[i] != expected {
t.Errorf("Uint16s RotateAllRight(19) at %d: got %d, want %d", i, buf[i], expected)
}
}
}
func TestUint32s(t *testing.T) {
in1 := make([]uint32, 16)
for i := range in1 {
in1[i] = uint32((i + 1) * 1000)
}
x := simd.LoadUint32s(in1)
buf := make([]uint32, x.Len())
// Test RotateAllLeft
rotLeft := x.RotateAllLeft(5)
rotLeft.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := in1[i]
expected := (val << 5) | (val >> 27)
if buf[i] != expected {
t.Errorf("Uint32s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
}
}
// Test RotateAllRight with large distance
rotRight := x.RotateAllRight(37)
rotRight.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := in1[i]
expected := (val >> 5) | (val << 27)
if buf[i] != expected {
t.Errorf("Uint32s RotateAllRight(37) at %d: got %d, want %d", i, buf[i], expected)
}
}
}
func TestUint64s(t *testing.T) {
in1 := make([]uint64, 8)
for i := range in1 {
in1[i] = uint64((i + 1) * 10000)
}
x := simd.LoadUint64s(in1)
buf := make([]uint64, x.Len())
// Test RotateAllLeft
rotLeft := x.RotateAllLeft(7)
rotLeft.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := in1[i]
expected := (val << 7) | (val >> 57)
if buf[i] != expected {
t.Errorf("Uint64s RotateAllLeft at %d: got %d, want %d", i, buf[i], expected)
}
}
// Test RotateAllRight with large distance
rotRight := x.RotateAllRight(71)
rotRight.Store(buf)
for i := 0; i < x.Len() && i < len(in1); i++ {
val := in1[i]
expected := (val >> 7) | (val << 57)
if buf[i] != expected {
t.Errorf("Uint64s RotateAllRight(71) at %d: got %d, want %d", i, buf[i], expected)
}
}
}
type HasStoreLen[E number] interface {
Store(s []E)
Len() int
}
func testBroadcast[E number, V HasStoreLen[E]](t *testing.T, x E, f func(e E) V) {
v := f(x)
s := make([]E, v.Len())
v.Store(s)
for _, e := range s {
if e != x {
t.Errorf("Expected %v, saw %v", x, e)
}
}
}
func TestBroadcast(t *testing.T) {
testBroadcast(t, int8(-2), simd.BroadcastInt8s)
testBroadcast(t, int16(-2), simd.BroadcastInt16s)
testBroadcast(t, int32(-2), simd.BroadcastInt32s)
testBroadcast(t, int64(-2), simd.BroadcastInt64s)
testBroadcast(t, uint8(99), simd.BroadcastUint8s)
testBroadcast(t, uint16(9999), simd.BroadcastUint16s)
testBroadcast(t, uint32(99991111), simd.BroadcastUint32s)
testBroadcast(t, uint64(112233445599887766), simd.BroadcastUint64s)
testBroadcast(t, float32(99991111), simd.BroadcastFloat32s)
testBroadcast(t, float64(112233445599887766), simd.BroadcastFloat64s)
}