Source file src/simd/archsimd/internal/simd_test/helpers_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
     6  
     7  package simd_test
     8  
     9  import (
    10  	"math"
    11  	"simd/archsimd/internal/test_helpers"
    12  	"testing"
    13  )
    14  
    15  type signed interface {
    16  	~int | ~int8 | ~int16 | ~int32 | ~int64
    17  }
    18  
    19  type integer interface {
    20  	~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
    21  }
    22  
    23  type float interface {
    24  	~float32 | ~float64
    25  }
    26  
    27  type number interface {
    28  	~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64
    29  }
    30  
    31  func checkSlices[T number](t *testing.T, got, want []T) bool {
    32  	t.Helper()
    33  	return test_helpers.CheckSlicesLogInput[T](t, got, want, 0.0, nil)
    34  }
    35  
    36  func checkSlicesLogInput[T number](t *testing.T, got, want []T, flakiness float64, logInput func()) bool {
    37  	t.Helper()
    38  	return test_helpers.CheckSlicesLogInput[T](t, got, want, flakiness, logInput)
    39  }
    40  
    41  // sliceOf returns a slice n T's, with each
    42  // element of the slice initialized to its
    43  // index + 1.
    44  func sliceOf[T number](n int) []T {
    45  	s := make([]T, n)
    46  	for i := 0; i < n; i++ {
    47  		s[i] = T(i + 1)
    48  	}
    49  	return s
    50  }
    51  
    52  func toVect[T signed](b []bool) []T {
    53  	s := make([]T, len(b))
    54  	for i := range b {
    55  		if b[i] {
    56  			s[i] = -1
    57  		}
    58  	}
    59  	return s
    60  }
    61  
    62  // s64 converts a slice of some integer type into a slice of int64
    63  func s64[T number](s []T) []int64 {
    64  	var is any = s
    65  	if r, ok := is.([]int64); ok {
    66  		return r
    67  	}
    68  	r := make([]int64, len(s))
    69  	for i := range s {
    70  		r[i] = int64(s[i])
    71  	}
    72  	return r
    73  }
    74  
    75  // Do implements slice part testing.  It repeatedly calls
    76  // body on smaller and smaller slices and an output slice
    77  // for the result, then compares the result to its own
    78  // calculation of what the result should be.
    79  func Do[T number](t *testing.T, n int, body func(a, c []T)) {
    80  	a := sliceOf[T](n)
    81  	b := sliceOf[T](n)
    82  
    83  	for i := n; i >= 0; i-- {
    84  		c := make([]T, n, n)
    85  		body(a[:i], c)
    86  		checkSlices(t, c, b)
    87  		if i > 0 {
    88  			b[i-1] = T(0)
    89  		}
    90  	}
    91  }
    92  
    93  // map3 returns a function that returns the slice of the results of applying
    94  // input parameter elem to the respective elements of its 3 slice inputs.
    95  func map3[T, U any](elem func(x, y, z T) U) func(x, y, z []T) []U {
    96  	return func(x, y, z []T) []U {
    97  		s := make([]U, len(x))
    98  		for i := range s {
    99  			s[i] = elem(x[i], y[i], z[i])
   100  		}
   101  		return s
   102  	}
   103  }
   104  
   105  // map2 returns a function that returns the slice of the results of applying
   106  // input parameter elem to the respective elements of its 2 slice inputs.
   107  func map2[T, U any](elem func(x, y T) U) func(x, y []T) []U {
   108  	return func(x, y []T) []U {
   109  		s := make([]U, len(x))
   110  		for i := range s {
   111  			s[i] = elem(x[i], y[i])
   112  		}
   113  		return s
   114  	}
   115  }
   116  
   117  // map1 returns a function that returns the slice of the results of applying
   118  // input parameter elem to the respective elements of its single slice input.
   119  func map1[T, U any](elem func(x T) U) func(x []T) []U {
   120  	return func(x []T) []U {
   121  		s := make([]U, len(x))
   122  		for i := range s {
   123  			s[i] = elem(x[i])
   124  		}
   125  		return s
   126  	}
   127  }
   128  
   129  // map1n returns a function that returns the slice of the results of applying
   130  // input parameter elem to the respective elements of its single slice input,
   131  // extended (with zero values) or truncated to length n.
   132  func map1n[T, U any](elem func(x T) U, n int) func(x []T) []U {
   133  	return func(x []T) []U {
   134  		s := make([]U, n)
   135  		for i := range min(len(x), n) {
   136  			s[i] = elem(x[i])
   137  		}
   138  		return s
   139  	}
   140  }
   141  
   142  // mapCompare returns a function that returns the slice of the results of applying
   143  // comparison function elem to the respective elements of its two slice inputs,
   144  // and returns -1 if the comparison is true, 0 otherwise.
   145  func mapCompare[T number](elem func(x, y T) bool) func(x, y []T) []int64 {
   146  	return func(x, y []T) []int64 {
   147  		s := make([]int64, len(x))
   148  		for i := range s {
   149  			if elem(x[i], y[i]) {
   150  				s[i] = -1
   151  			}
   152  		}
   153  		return s
   154  	}
   155  }
   156  
   157  // nOf returns a slice of length n whose elements are taken
   158  // from input slice s.
   159  func nOf[T any](n int, s []T) []T {
   160  	if len(s) >= n {
   161  		return s
   162  	}
   163  	r := make([]T, n)
   164  	for i := range r {
   165  		r[i] = s[i%len(s)]
   166  	}
   167  	return r
   168  }
   169  
   170  const (
   171  	PN22  = 1.0 / 1024 / 1024 / 4
   172  	PN24  = 1.0 / 1024 / 1024 / 16
   173  	PN53  = PN24 * PN24 / 32
   174  	F0    = float32(1.0 + 513*PN22/2)
   175  	F1    = float32(1.0 + 511*PN22*8)
   176  	Aeasy = float32(2046 * PN53)
   177  	Ahard = float32(2047 * PN53) // 2047 provokes a 2-rounding in 64-bit FMA rounded to 32-bit
   178  )
   179  
   180  var zero = 0.0
   181  var nzero = -zero
   182  var inf = 1 / zero
   183  var ninf = -1 / zero
   184  var nan = math.NaN()
   185  var snan32 = math.Float32frombits(0x7f800001)
   186  var snan64 = math.Float64frombits(0x7ff0000000000001)
   187  
   188  // N controls how large the test vectors are
   189  const N = 144
   190  
   191  var float32s = nOf(N, []float32{float32(inf), float32(ninf), 1, float32(nan), snan32, -float32(nan), -snan32, float32(zero), 2, float32(nan), float32(zero), 3, float32(-zero), float32(1.0 / zero), float32(-1.0 / zero), 1.0 / 2, 1.0 / 4, 1.0 / 8, 1.0 / 1000, 1.0 / 1000000, 1, -1, 0, 2, -2, 3, -3, math.MaxFloat32, 1 / math.MaxFloat32, 10, -10, 100, 20, -20, 300, -300, -4000, -80, -160, -3200, -64, -4, -8, -16, -32, -64})
   192  var float64s = nOf(N, []float64{inf, ninf, nan, snan64, -nan, -snan64, zero, -zero, 1 / zero, -1 / zero, 0.0001, 0.0000001, 1, -1, 0, 2, -2, 3, -3, math.MaxFloat64, 1.0 / math.MaxFloat64, 10, -10, 100, 20, -20, 300, -300, -4000, -80, -16, -32, -64})
   193  
   194  var int32s = nOf(N, []int32{1, -1, 0, 2, 4, 8, 1024, 0xffffff, -0xffffff, 0x55555, 0x77777, 0xccccc, -0x55555, -0x77777, -0xccccc, -4, -8, -16, -32, -64})
   195  var uint32s = nOf(N, []uint32{1, 0, 2, 4, 8, 1024, 0xffffff, ^uint32(0xffffff), 0x55555, 0x77777, 0xccccc, ^uint32(0x55555), ^uint32(0x77777), ^uint32(0xccccc)})
   196  
   197  var int64s = nOf(N, []int64{1, -1, 0, 2, 4, 8, 1024, 0xffffff, -0xffffff, 0x55555, 0x77777, 0xccccc, -0x55555, -0x77777, -0xccccc, -4, -8, -16, -32, -64})
   198  var uint64s = nOf(N, []uint64{1, 0, 2, 4, 8, 1024, 0xffffff, ^uint64(0xffffff), 0x55555, 0x77777, 0xccccc, ^uint64(0x55555), ^uint64(0x77777), ^uint64(0xccccc)})
   199  
   200  var int16s = nOf(N, []int16{1, -1, 0, 2, 4, 8, 1024, 3, 5, 7, 11, 13, 3000, 5555, 7777, 11111, 32767, 32766, -32767, -32768, -11111, -4, -8, -16, -32, -64})
   201  var uint16s = nOf(N, []uint16{1, 0, 2, 4, 8, 1024, 3, 5, 7, 11, 13, 3000, 5555, 7777, 11111, 32767, 32766, 32768, 65535, 45678, 56789})
   202  
   203  var int8s = nOf(N, []int8{0, 1, 2, 3, 5, 7, 11, 22, 33, 55, 77, 121, 127, -1, -2, -3, -5, -7, -11, -77, -121, -127, -128, 4, 8, 16, 32, 64, -4, -8, -16, -32, -64})
   204  var uint8s = nOf(N, []uint8{0, 1, 2, 3, 5, 7, 11, 22, 33, 55, 77, 121, 127, 128, 255, 233, 211, 177, 144, 4, 8, 16, 32, 64})
   205  
   206  var bools = nOf(N, []bool{
   207  	true, false, true, true, false, false, true, true, true, false, false, false, true, true, true, true, false, false, false, false})
   208  
   209  func forSlice[T number](t *testing.T, s []T, n int, f func(a []T) bool) {
   210  	t.Helper()
   211  	for i := 0; i < len(s)-n; i++ {
   212  		if !f(s[i : i+n]) {
   213  			return
   214  		}
   215  	}
   216  }
   217  
   218  func forSlicePair[T number](t *testing.T, s []T, n int, f func(a, b []T) bool) {
   219  	t.Helper()
   220  	for i := 0; i < len(s)-n; i++ {
   221  		for j := 0; j < len(s)-n; j++ {
   222  			if !f(s[i:i+n], s[j:j+n]) {
   223  				return
   224  			}
   225  		}
   226  	}
   227  }
   228  
   229  func forSliceTriple[T number](t *testing.T, s []T, n int, f func(a, b, c []T) bool) {
   230  	t.Helper()
   231  	for i := 0; i < len(s)-n; i += 3 {
   232  		for j := 0; j < len(s)-n; j += 3 {
   233  			for k := 0; k < len(s)-n; k += 3 {
   234  				if !f(s[i:i+n], s[j:j+n], s[k:k+n]) {
   235  					return
   236  				}
   237  			}
   238  		}
   239  	}
   240  }
   241  
   242  func forSlicePairMasked[T number](t *testing.T, s []T, n int, f func(a, b []T, m []bool) bool) {
   243  	t.Helper()
   244  	m := bools
   245  	// Step slice pair masked forward much more quickly, otherwise it is slooooow
   246  	for i := 0; i < len(s)-n; i += 3 {
   247  		for j := 0; j < len(s)-n; j += 3 {
   248  			for k := 0; k < len(m)-n; k += 3 {
   249  				if !f(s[i:i+n], s[j:j+n], m[k:k+n]) {
   250  					return
   251  				}
   252  			}
   253  		}
   254  	}
   255  }
   256  

View as plain text