Source file src/simd/internal/test_helpers/emulation.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 test_helpers
     8  
     9  import (
    10  	"math/bits"
    11  	"testing"
    12  	"unsafe"
    13  )
    14  
    15  func ReduceSum[S number](s []S) S {
    16  	var x S
    17  	for _, v := range s {
    18  		x += v
    19  	}
    20  	return x
    21  }
    22  
    23  func Neg[S number](x S) S {
    24  	return -x
    25  }
    26  
    27  func Abs[S, S2 number](x S) S2 {
    28  	if x == 0 {
    29  		return 0 // this is zero not -0 if S is float
    30  	}
    31  	if x < 0 {
    32  		return S2(-x)
    33  	}
    34  	return S2(x)
    35  }
    36  
    37  func Add[S number](x, y S) S {
    38  	return x + y
    39  }
    40  
    41  func Sub[S number](x, y S) S {
    42  	return x - y
    43  }
    44  
    45  func Mul[S number](x, y S) S {
    46  	return x * y
    47  }
    48  
    49  func Div[S float](x, y S) S {
    50  	return x / y
    51  }
    52  
    53  func And[S integer](x, y S) S {
    54  	return x & y
    55  }
    56  
    57  func Or[S integer](x, y S) S {
    58  	return x | y
    59  }
    60  
    61  func Xor[S integer](x, y S) S {
    62  	return x ^ y
    63  }
    64  
    65  func AndNot[S integer](x, y S) S {
    66  	return x &^ y
    67  }
    68  
    69  func Xnor[S integer](x, y S) S {
    70  	return ^(x ^ y)
    71  }
    72  
    73  func Not[S integer](x S) S {
    74  	return ^x
    75  }
    76  
    77  func Max[S number](x, y S) S {
    78  	return max(x, y)
    79  }
    80  
    81  func Min[S number](x, y S) S {
    82  	return min(x, y)
    83  }
    84  
    85  func ShiftLeft[S integer](x S, s uint64) S {
    86  	return x << s
    87  }
    88  
    89  func ShiftRight[S integer](x S, s uint64) S {
    90  	return x >> s
    91  }
    92  
    93  func Equal[S number](x, y S) bool {
    94  	return x == y
    95  }
    96  
    97  func NotEqual[S number](x, y S) bool {
    98  	return x != y
    99  }
   100  
   101  func Less[S number](x, y S) bool {
   102  	return x < y
   103  }
   104  
   105  func LessEqual[S number](x, y S) bool {
   106  	return x <= y
   107  }
   108  
   109  func Greater[S number](x, y S) bool {
   110  	return x > y
   111  }
   112  
   113  func GreaterEqual[S number](x, y S) bool {
   114  	return x >= y
   115  }
   116  
   117  func RotateLeft[S integer](x S, dist uint64) S {
   118  	size := uint64(unsafe.Sizeof(x)) * 8
   119  	mask := (uint64(1) << size) - 1
   120  	dist = dist & (size - 1)
   121  	if dist == 0 {
   122  		return x
   123  	}
   124  	return (x << dist) | S((mask&uint64(x))>>(size-dist))
   125  }
   126  
   127  func RotateRight[S integer](x S, dist uint64) S {
   128  	size := uint64(unsafe.Sizeof(x)) * 8
   129  	mask := (uint64(1) << size) - 1
   130  	dist = dist & (size - 1)
   131  	if dist == 0 {
   132  		return x
   133  	}
   134  	return S((mask&uint64(x))>>dist) | (x << (size - dist))
   135  }
   136  
   137  func OnesCount[T integer](x T) T {
   138  	size := uint64(unsafe.Sizeof(x)) * 8
   139  	return T(bits.OnesCount64(uint64(x) & ((1 << size) - 1)))
   140  }
   141  
   142  func TrailingZeros[T integer](x T) T {
   143  	size := int(unsafe.Sizeof(x)) * 8
   144  	tz := bits.TrailingZeros64(uint64(x))
   145  	if tz > size {
   146  		tz = size
   147  	}
   148  	return T(tz)
   149  }
   150  
   151  // Vec is any vector of any number (int, uint, or float)
   152  type Vec[E number] interface {
   153  	Store(s []E)
   154  	Len() int
   155  }
   156  
   157  // Mask is any mask
   158  type Mask interface {
   159  	String() string
   160  }
   161  
   162  // IVec is a vector of signed integers, that will also have a ToMask() method.
   163  type IVec[E signed, M Mask] interface {
   164  	Store(s []E)
   165  	Len() int
   166  	ToMask() M
   167  }
   168  
   169  // The Test-XX-2-YY-[e] functions test various unary and binary vector operations.
   170  // V = 1 vector, VV = 2 vectors, VR = vector and rotation.
   171  // S = scalar.
   172  // M = mask.
   173  // A trailing "e" means that the emulation function is elementwise, or scalar.
   174  
   175  // TestV2S tests the simd Vector-To-Scalar method f against the expected behavior generated by want
   176  func TestV2S[S number, V Vec[S]](t *testing.T, slice []S, load func([]S) V, f func(_ V) S, want func([]S) S) {
   177  	var v V
   178  	n := v.Len()
   179  	t.Helper()
   180  	forSlice(t, slice, n, func(x []S) bool {
   181  		t.Helper()
   182  		a := load(x)
   183  		g := f(a)
   184  		w := want(x)
   185  		return CheckScalarsLogInput(t, g, w, 0.000001, func() { t.Helper(); t.Logf("x=%v", x) })
   186  	})
   187  }
   188  
   189  // TestV2V tests the simd Vector-To-Vector method f against the expected behavior generated by want
   190  func TestV2V[S number, V Vec[S]](t *testing.T, slice []S, load func([]S) V, f func(_ V) V, want func([]S) []S) {
   191  	var v V
   192  	n := v.Len()
   193  	t.Helper()
   194  	forSlice(t, slice, n, func(x []S) bool {
   195  		t.Helper()
   196  		a := load(x)
   197  		g := make([]S, n)
   198  		f(a).Store(g)
   199  		w := want(x)
   200  		return CheckSlicesLogInput(t, g, w, 0.000001, func() { t.Helper(); t.Logf("x=%v", x) })
   201  	})
   202  }
   203  
   204  // TestV2Ve tests the simd unary Vector-To-Vector method f against the expected behavior generated by per-Element function want
   205  func TestV2Ve[S, S2 number, V Vec[S], V2 Vec[S2]](t *testing.T, slice []S, load func([]S) V, f func(_ V) V2, want func(S) S2) {
   206  	var v V
   207  	n := v.Len()
   208  	t.Helper()
   209  	forSlice(t, slice, n, func(x []S) bool {
   210  		t.Helper()
   211  		a := load(x)
   212  		g := make([]S2, n)
   213  		f(a).Store(g)
   214  		w := ImpliedDo(n, func(i int) S2 { return want(x[i]) })
   215  		return CheckSlicesLogInput(t, g, w, 0.000001, func() { t.Helper(); t.Logf("x=%v", x) })
   216  	})
   217  }
   218  
   219  // TestVR2Ve tests the simd binary (Vector,Rotation)-To-Vector method f against the expected behavior generated by per-Element function want
   220  func TestVR2Ve[S number, V Vec[S]](t *testing.T, slice []S, shifts []uint64, load func([]S) V, f func(V, uint64) V, want func(S, uint64) S) {
   221  	var v V
   222  	n := v.Len()
   223  	t.Helper()
   224  	for _, shift := range shifts {
   225  		forSlice(t, slice, n, func(x []S) bool {
   226  			t.Helper()
   227  			a := load(x)
   228  			g := make([]S, n)
   229  			f(a, shift).Store(g)
   230  			w := ImpliedDo(n, func(i int) S { return want(x[i], shift) })
   231  			return CheckSlicesLogInput(t, g, w, 0.000001, func() { t.Helper(); t.Logf("x=%v, dist=%v", x, shift) })
   232  		})
   233  	}
   234  }
   235  
   236  // TestVV2V tests the simd binary (vector,vector)-to-vector method f against the expected behavior generated by want
   237  func TestVV2V[S number, V Vec[S]](t *testing.T, slice []S, load func([]S) V, f func(_, _ V) V, want func(_, _ []S) []S) {
   238  	var v V
   239  	n := v.Len()
   240  	t.Helper()
   241  	forSlicePair(t, slice, n, func(x, y []S) bool {
   242  		t.Helper()
   243  		a := load(x)
   244  		b := load(y)
   245  		g := make([]S, n)
   246  		f(a, b).Store(g)
   247  		w := want(x, y)
   248  		return CheckSlicesLogInput(t, g, w, 0.000001, func() { t.Helper(); t.Logf("x=%v, y=%v", x, y) })
   249  	})
   250  }
   251  
   252  // TestVV2Ve tests the simd binary (Vector,Vector)-To-Vector method f against the expected behavior generated by per-Element function want
   253  func TestVV2Ve[S number, V Vec[S]](t *testing.T, slice []S, load func([]S) V, f func(_, _ V) V, want func(_, _ S) S) {
   254  	var v V
   255  	n := v.Len()
   256  	t.Helper()
   257  	forSlicePair(t, slice, n, func(x, y []S) bool {
   258  		t.Helper()
   259  		a := load(x)
   260  		b := load(y)
   261  		g := make([]S, n)
   262  		f(a, b).Store(g)
   263  		w := ImpliedDo(n, func(i int) S { return want(x[i], y[i]) })
   264  		return CheckSlicesLogInput(t, g, w, 0.000001, func() { t.Helper(); t.Logf("x=%v, y=%v", x, y) })
   265  	})
   266  }
   267  
   268  // TestVV2Me tests the simd comparison method f against the expected behavior generated by per-Element function want
   269  func TestVV2Me[S number, V Vec[S], M Mask](t *testing.T, slice []S, load func([]S) V, f func(_, _ V) M, want func(_, _ S) bool) {
   270  	var v V
   271  	n := v.Len()
   272  	t.Helper()
   273  	forSlicePair(t, slice, n, func(x, y []S) bool {
   274  		t.Helper()
   275  		a := load(x)
   276  		b := load(y)
   277  		g := f(a, b).String()
   278  		w := ImpliedBools(n, func(i int) bool { return want(x[i], y[i]) })
   279  		return CheckStringsLogInput(t, g, w, 0.000001, func() { t.Helper(); t.Logf("x=%v, y=%v", x, y) })
   280  	})
   281  }
   282  
   283  // TestM2Me tests the simd mask unary method f against the expected behavior generated by per-Element function want
   284  func TestM2Me[S signed, M Mask, V IVec[S, M]](t *testing.T, load func([]S) V, f func(_ M) M, want func(_ S) S) {
   285  	var v V
   286  	n := v.Len()
   287  	t.Helper()
   288  	b2s := func(b bool) S {
   289  		if b {
   290  			return -1
   291  		}
   292  		return 0
   293  	}
   294  
   295  	bload := func(s []bool) V {
   296  		sl := make([]S, n)
   297  		for i := range sl {
   298  			sl[i] = b2s(s[i])
   299  		}
   300  		return load(sl)
   301  	}
   302  	forBoolSlice(t, Bools(), n, func(x []bool) bool {
   303  		t.Helper()
   304  		a := bload(x)
   305  		g := f(a.ToMask()).String()
   306  		w := ImpliedBools(n, func(i int) bool { return 0 != want(b2s(x[i])) })
   307  		return CheckStringsLogInput(t, g, w, 0.000001, func() { t.Helper(); t.Logf("x=%v", x) })
   308  	})
   309  }
   310  
   311  // TestMM2Me tests the simd mask binary method f against the expected behavior generated by per-Element function want
   312  func TestMM2Me[S signed, M Mask, V IVec[S, M]](t *testing.T, load func([]S) V, f func(_, _ M) M, want func(_, _ S) S) {
   313  	var v V
   314  	n := v.Len()
   315  	t.Helper()
   316  	b2s := func(b bool) S {
   317  		if b {
   318  			return -1
   319  		}
   320  		return 0
   321  	}
   322  
   323  	bload := func(s []bool) V {
   324  		sl := make([]S, n)
   325  		for i := range sl {
   326  			sl[i] = b2s(s[i])
   327  		}
   328  		return load(sl)
   329  	}
   330  	forBoolSlicePair(t, Bools(), n, func(x, y []bool) bool {
   331  		t.Helper()
   332  		a := bload(x)
   333  		b := bload(y)
   334  		g := f(a.ToMask(), b.ToMask()).String()
   335  		w := ImpliedBools(n, func(i int) bool { return 0 != want(b2s(x[i]), b2s(y[i])) })
   336  		return CheckStringsLogInput(t, g, w, 0.000001, func() { t.Helper(); t.Logf("x=%v, y=%v", x, y) })
   337  	})
   338  }
   339  

View as plain text