Source file src/simd/archsimd/internal/simd_test/simulation_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  )
    12  
    13  func less[T number](x, y T) bool {
    14  	return x < y
    15  }
    16  func lessEqual[T number](x, y T) bool {
    17  	return x <= y
    18  }
    19  func greater[T number](x, y T) bool {
    20  	return x > y
    21  }
    22  func greaterEqual[T number](x, y T) bool {
    23  	return x >= y
    24  }
    25  func equal[T number](x, y T) bool {
    26  	return x == y
    27  }
    28  func notEqual[T number](x, y T) bool {
    29  	return x != y
    30  }
    31  
    32  func isNaN[T float](x T) bool {
    33  	return x != x
    34  }
    35  
    36  func abs[T number](x T) T {
    37  	// TODO this will need a non-standard FP-equality test.
    38  	if x == 0 { // true if x is -0.
    39  		return 0 // this is not a negative zero
    40  	}
    41  	if x < 0 {
    42  		return -x
    43  	}
    44  	return x
    45  }
    46  
    47  func ceil[T float](x T) T {
    48  	return T(math.Ceil(float64(x)))
    49  }
    50  func floor[T float](x T) T {
    51  	return T(math.Floor(float64(x)))
    52  }
    53  func not[T integer](x T) T {
    54  	return ^x
    55  }
    56  func round[T float](x T) T {
    57  	return T(math.RoundToEven(float64(x)))
    58  }
    59  func sqrt[T float](x T) T {
    60  	return T(math.Sqrt(float64(x)))
    61  }
    62  func trunc[T float](x T) T {
    63  	return T(math.Trunc(float64(x)))
    64  }
    65  
    66  func add[T number](x, y T) T {
    67  	return x + y
    68  }
    69  
    70  func sub[T number](x, y T) T {
    71  	return x - y
    72  }
    73  
    74  func max_[T number](x, y T) T { // "max" lands in infinite recursion
    75  	return max(x, y)
    76  }
    77  
    78  func min_[T number](x, y T) T { // "min" lands in infinite recursion
    79  	return min(x, y)
    80  }
    81  
    82  // Also mulLow for integers
    83  func mul[T number](x, y T) T {
    84  	return x * y
    85  }
    86  
    87  func div[T number](x, y T) T {
    88  	return x / y
    89  }
    90  
    91  func and[T integer](x, y T) T {
    92  	return x & y
    93  }
    94  
    95  func andNotI[T integer](x, y T) T {
    96  	return x & ^y // order corrected to match expectations
    97  }
    98  
    99  func orI[T integer](x, y T) T {
   100  	return x | y
   101  }
   102  
   103  func xorI[T integer](x, y T) T {
   104  	return x ^ y
   105  }
   106  
   107  func ima[T integer](x, y, z T) T {
   108  	return x*y + z
   109  }
   110  
   111  func fma[T float](x, y, z T) T {
   112  	return T(math.FMA(float64(x), float64(y), float64(z)))
   113  }
   114  
   115  func toUint8[T number](x T) uint8 {
   116  	return uint8(x)
   117  }
   118  
   119  func toUint16[T number](x T) uint16 {
   120  	return uint16(x)
   121  }
   122  
   123  func toUint64[T number](x T) uint64 {
   124  	return uint64(x)
   125  }
   126  
   127  func toUint32[T number](x T) uint32 {
   128  	return uint32(x)
   129  }
   130  
   131  func toInt8[T number](x T) int8 {
   132  	return int8(x)
   133  }
   134  
   135  func toInt16[T number](x T) int16 {
   136  	return int16(x)
   137  }
   138  
   139  func toInt32[T number](x T) int32 {
   140  	return int32(x)
   141  }
   142  
   143  func toInt64[T number](x T) int64 {
   144  	return int64(x)
   145  }
   146  
   147  func toFloat32[T number](x T) float32 {
   148  	return float32(x)
   149  }
   150  
   151  func toFloat64[T number](x T) float64 {
   152  	return float64(x)
   153  }
   154  
   155  // X86 specific behavior for conversion from float to int32.
   156  // If the value cannot be represented as int32, it returns -0x80000000.
   157  func floatToInt32_x86[T float](x T) int32 {
   158  	switch y := (any(x)).(type) {
   159  	case float32:
   160  		if y != y || y < math.MinInt32 ||
   161  			y >= math.MaxInt32 { // float32(MaxInt32) == 0x80000000, actually overflows
   162  			return -0x80000000
   163  		}
   164  	case float64:
   165  		if y != y || y < math.MinInt32 ||
   166  			y > math.MaxInt32 { // float64(MaxInt32) is exact, no overflow
   167  			return -0x80000000
   168  		}
   169  	}
   170  	return int32(x)
   171  }
   172  
   173  // X86 specific behavior for conversion from float to int64.
   174  // If the value cannot be represented as int64, it returns -0x80000000_00000000.
   175  func floatToInt64_x86[T float](x T) int64 {
   176  	switch y := (any(x)).(type) {
   177  	case float32:
   178  		if y != y || y < math.MinInt64 ||
   179  			y >= math.MaxInt64 { // float32(MaxInt64) == 0x80000000_00000000, actually overflows
   180  			return -0x80000000_00000000
   181  		}
   182  	case float64:
   183  		if y != y || y < math.MinInt64 ||
   184  			y >= math.MaxInt64 { // float64(MaxInt64) == 0x80000000_00000000, also overflows
   185  			return -0x80000000_00000000
   186  		}
   187  	}
   188  	return int64(x)
   189  }
   190  
   191  // X86 specific behavior for conversion from float to uint32.
   192  // If the value cannot be represented as uint32, it returns 1<<32 - 1.
   193  func floatToUint32_x86[T float](x T) uint32 {
   194  	switch y := (any(x)).(type) {
   195  	case float32:
   196  		if y < 0 || y > math.MaxUint32 || y != y {
   197  			return 1<<32 - 1
   198  		}
   199  	case float64:
   200  		if y < 0 || y > math.MaxUint32 || y != y {
   201  			return 1<<32 - 1
   202  		}
   203  	}
   204  	return uint32(x)
   205  }
   206  
   207  // X86 specific behavior for conversion from float to uint64.
   208  // If the value cannot be represented as uint64, it returns 1<<64 - 1.
   209  func floatToUint64_x86[T float](x T) uint64 {
   210  	switch y := (any(x)).(type) {
   211  	case float32:
   212  		if y < 0 || y > math.MaxUint64 || y != y {
   213  			return 1<<64 - 1
   214  		}
   215  	case float64:
   216  		if y < 0 || y > math.MaxUint64 || y != y {
   217  			return 1<<64 - 1
   218  		}
   219  	}
   220  	return uint64(x)
   221  }
   222  
   223  func ceilResidueForPrecision[T float](i int) func(T) T {
   224  	f := 1.0
   225  	for i > 0 {
   226  		f *= 2
   227  		i--
   228  	}
   229  	return func(x T) T {
   230  		y := float64(x)
   231  		if math.IsInf(float64(x*T(f)), 0) {
   232  			return 0
   233  		}
   234  		// TODO sort out the rounding issues when T === float32
   235  		return T(y - math.Ceil(y*f)/f)
   236  	}
   237  }
   238  
   239  // Slice versions of all these elementwise operations
   240  
   241  func addSlice[T number](x, y []T) []T {
   242  	return map2[T](add)(x, y)
   243  }
   244  
   245  func subSlice[T number](x, y []T) []T {
   246  	return map2[T](sub)(x, y)
   247  }
   248  
   249  func maxSlice[T number](x, y []T) []T {
   250  	return map2[T](max_)(x, y)
   251  }
   252  
   253  func minSlice[T number](x, y []T) []T {
   254  	return map2[T](min_)(x, y)
   255  }
   256  
   257  // mulLow for integers
   258  func mulSlice[T number](x, y []T) []T {
   259  	return map2[T](mul)(x, y)
   260  }
   261  
   262  func divSlice[T number](x, y []T) []T {
   263  	return map2[T](div)(x, y)
   264  }
   265  
   266  func andSlice[T integer](x, y []T) []T {
   267  	return map2[T](and)(x, y)
   268  }
   269  
   270  func andNotSlice[T integer](x, y []T) []T {
   271  	return map2[T](andNotI)(x, y)
   272  }
   273  
   274  func orSlice[T integer](x, y []T) []T {
   275  	return map2[T](orI)(x, y)
   276  }
   277  
   278  func xorSlice[T integer](x, y []T) []T {
   279  	return map2[T](xorI)(x, y)
   280  }
   281  
   282  func lessSlice[T number](x, y []T) []int64 {
   283  	return mapCompare[T](less)(x, y)
   284  }
   285  
   286  func lessEqualSlice[T number](x, y []T) []int64 {
   287  	return mapCompare[T](lessEqual)(x, y)
   288  }
   289  
   290  func greaterSlice[T number](x, y []T) []int64 {
   291  	return mapCompare[T](greater)(x, y)
   292  }
   293  
   294  func greaterEqualSlice[T number](x, y []T) []int64 {
   295  	return mapCompare[T](greaterEqual)(x, y)
   296  }
   297  
   298  func equalSlice[T number](x, y []T) []int64 {
   299  	return mapCompare[T](equal)(x, y)
   300  }
   301  
   302  func notEqualSlice[T number](x, y []T) []int64 {
   303  	return mapCompare[T](notEqual)(x, y)
   304  }
   305  
   306  func isNaNSlice[T float](x []T) []int64 {
   307  	return map1[T](func(x T) int64 {
   308  		if isNaN(x) {
   309  			return -1
   310  		}
   311  		return 0
   312  	})(x)
   313  }
   314  
   315  func ceilSlice[T float](x []T) []T {
   316  	return map1[T](ceil)(x)
   317  }
   318  
   319  func floorSlice[T float](x []T) []T {
   320  	return map1[T](floor)(x)
   321  }
   322  
   323  func notSlice[T integer](x []T) []T {
   324  	return map1[T](not)(x)
   325  }
   326  
   327  func roundSlice[T float](x []T) []T {
   328  	return map1[T](round)(x)
   329  }
   330  
   331  func sqrtSlice[T float](x []T) []T {
   332  	return map1[T](sqrt)(x)
   333  }
   334  
   335  func truncSlice[T float](x []T) []T {
   336  	return map1[T](trunc)(x)
   337  }
   338  
   339  func imaSlice[T integer](x, y, z []T) []T {
   340  	return map3[T](ima)(x, y, z)
   341  }
   342  
   343  func fmaSlice[T float](x, y, z []T) []T {
   344  	return map3[T](fma)(x, y, z)
   345  }
   346  
   347  func satToInt8[T integer](x T) int8 {
   348  	var m int8 = -128
   349  	var M int8 = 127
   350  	if T(M) < T(m) { // expecting T being a larger type
   351  		panic("bad input type")
   352  	}
   353  	if x < T(m) {
   354  		return m
   355  	}
   356  	if x > T(M) {
   357  		return M
   358  	}
   359  	return int8(x)
   360  }
   361  
   362  func satToUint8[T integer](x T) uint8 {
   363  	var M uint8 = 255
   364  	if T(M) < 0 { // expecting T being a larger type
   365  		panic("bad input type")
   366  	}
   367  	if x < 0 {
   368  		return 0
   369  	}
   370  	if x > T(M) {
   371  		return M
   372  	}
   373  	return uint8(x)
   374  }
   375  
   376  func satToInt16[T integer](x T) int16 {
   377  	var m int16 = -32768
   378  	var M int16 = 32767
   379  	if T(M) < T(m) { // expecting T being a larger type
   380  		panic("bad input type")
   381  	}
   382  	if x < T(m) {
   383  		return m
   384  	}
   385  	if x > T(M) {
   386  		return M
   387  	}
   388  	return int16(x)
   389  }
   390  
   391  func satToUint16[T integer](x T) uint16 {
   392  	var M uint16 = 65535
   393  	if T(M) < 0 { // expecting T being a larger type
   394  		panic("bad input type")
   395  	}
   396  	if x < 0 {
   397  		return 0
   398  	}
   399  	if x > T(M) {
   400  		return M
   401  	}
   402  	return uint16(x)
   403  }
   404  
   405  func satToInt32[T integer](x T) int32 {
   406  	var m int32 = -1 << 31
   407  	var M int32 = 1<<31 - 1
   408  	if T(M) < T(m) { // expecting T being a larger type
   409  		panic("bad input type")
   410  	}
   411  	if x < T(m) {
   412  		return m
   413  	}
   414  	if x > T(M) {
   415  		return M
   416  	}
   417  	return int32(x)
   418  }
   419  
   420  func satToUint32[T integer](x T) uint32 {
   421  	var M uint32 = 1<<32 - 1
   422  	if T(M) < 0 { // expecting T being a larger type
   423  		panic("bad input type")
   424  	}
   425  	if x < 0 {
   426  		return 0
   427  	}
   428  	if x > T(M) {
   429  		return M
   430  	}
   431  	return uint32(x)
   432  }
   433  

View as plain text