// Copyright 2025 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 && amd64 package simd_test import ( "math" ) func less[T number](x, y T) bool { return x < y } func lessEqual[T number](x, y T) bool { return x <= y } func greater[T number](x, y T) bool { return x > y } func greaterEqual[T number](x, y T) bool { return x >= y } func equal[T number](x, y T) bool { return x == y } func notEqual[T number](x, y T) bool { return x != y } func isNaN[T float](x T) bool { return x != x } func abs[T number](x T) T { // TODO this will need a non-standard FP-equality test. if x == 0 { // true if x is -0. return 0 // this is not a negative zero } if x < 0 { return -x } return x } func ceil[T float](x T) T { return T(math.Ceil(float64(x))) } func floor[T float](x T) T { return T(math.Floor(float64(x))) } func not[T integer](x T) T { return ^x } func round[T float](x T) T { return T(math.RoundToEven(float64(x))) } func sqrt[T float](x T) T { return T(math.Sqrt(float64(x))) } func trunc[T float](x T) T { return T(math.Trunc(float64(x))) } func add[T number](x, y T) T { return x + y } func sub[T number](x, y T) T { return x - y } func max_[T number](x, y T) T { // "max" lands in infinite recursion return max(x, y) } func min_[T number](x, y T) T { // "min" lands in infinite recursion return min(x, y) } // Also mulLow for integers func mul[T number](x, y T) T { return x * y } func div[T number](x, y T) T { return x / y } func and[T integer](x, y T) T { return x & y } func andNotI[T integer](x, y T) T { return x & ^y // order corrected to match expectations } func orI[T integer](x, y T) T { return x | y } func xorI[T integer](x, y T) T { return x ^ y } func ima[T integer](x, y, z T) T { return x*y + z } func fma[T float](x, y, z T) T { return T(math.FMA(float64(x), float64(y), float64(z))) } func toUint8[T number](x T) uint8 { return uint8(x) } func toUint16[T number](x T) uint16 { return uint16(x) } func toUint64[T number](x T) uint64 { return uint64(x) } func toUint32[T number](x T) uint32 { return uint32(x) } func toInt8[T number](x T) int8 { return int8(x) } func toInt16[T number](x T) int16 { return int16(x) } func toInt32[T number](x T) int32 { return int32(x) } func toInt64[T number](x T) int64 { return int64(x) } func toFloat32[T number](x T) float32 { return float32(x) } func toFloat64[T number](x T) float64 { return float64(x) } // X86 specific behavior for conversion from float to int32. // If the value cannot be represented as int32, it returns -0x80000000. func floatToInt32_x86[T float](x T) int32 { switch y := (any(x)).(type) { case float32: if y != y || y < math.MinInt32 || y >= math.MaxInt32 { // float32(MaxInt32) == 0x80000000, actually overflows return -0x80000000 } case float64: if y != y || y < math.MinInt32 || y > math.MaxInt32 { // float64(MaxInt32) is exact, no overflow return -0x80000000 } } return int32(x) } // X86 specific behavior for conversion from float to int64. // If the value cannot be represented as int64, it returns -0x80000000_00000000. func floatToInt64_x86[T float](x T) int64 { switch y := (any(x)).(type) { case float32: if y != y || y < math.MinInt64 || y >= math.MaxInt64 { // float32(MaxInt64) == 0x80000000_00000000, actually overflows return -0x80000000_00000000 } case float64: if y != y || y < math.MinInt64 || y >= math.MaxInt64 { // float64(MaxInt64) == 0x80000000_00000000, also overflows return -0x80000000_00000000 } } return int64(x) } // X86 specific behavior for conversion from float to uint32. // If the value cannot be represented as uint32, it returns 1<<32 - 1. func floatToUint32_x86[T float](x T) uint32 { switch y := (any(x)).(type) { case float32: if y < 0 || y > math.MaxUint32 || y != y { return 1<<32 - 1 } case float64: if y < 0 || y > math.MaxUint32 || y != y { return 1<<32 - 1 } } return uint32(x) } // X86 specific behavior for conversion from float to uint64. // If the value cannot be represented as uint64, it returns 1<<64 - 1. func floatToUint64_x86[T float](x T) uint64 { switch y := (any(x)).(type) { case float32: if y < 0 || y > math.MaxUint64 || y != y { return 1<<64 - 1 } case float64: if y < 0 || y > math.MaxUint64 || y != y { return 1<<64 - 1 } } return uint64(x) } func ceilResidueForPrecision[T float](i int) func(T) T { f := 1.0 for i > 0 { f *= 2 i-- } return func(x T) T { y := float64(x) if math.IsInf(float64(x*T(f)), 0) { return 0 } // TODO sort out the rounding issues when T === float32 return T(y - math.Ceil(y*f)/f) } } // Slice versions of all these elementwise operations func addSlice[T number](x, y []T) []T { return map2[T](add)(x, y) } func subSlice[T number](x, y []T) []T { return map2[T](sub)(x, y) } func maxSlice[T number](x, y []T) []T { return map2[T](max_)(x, y) } func minSlice[T number](x, y []T) []T { return map2[T](min_)(x, y) } // mulLow for integers func mulSlice[T number](x, y []T) []T { return map2[T](mul)(x, y) } func divSlice[T number](x, y []T) []T { return map2[T](div)(x, y) } func andSlice[T integer](x, y []T) []T { return map2[T](and)(x, y) } func andNotSlice[T integer](x, y []T) []T { return map2[T](andNotI)(x, y) } func orSlice[T integer](x, y []T) []T { return map2[T](orI)(x, y) } func xorSlice[T integer](x, y []T) []T { return map2[T](xorI)(x, y) } func lessSlice[T number](x, y []T) []int64 { return mapCompare[T](less)(x, y) } func lessEqualSlice[T number](x, y []T) []int64 { return mapCompare[T](lessEqual)(x, y) } func greaterSlice[T number](x, y []T) []int64 { return mapCompare[T](greater)(x, y) } func greaterEqualSlice[T number](x, y []T) []int64 { return mapCompare[T](greaterEqual)(x, y) } func equalSlice[T number](x, y []T) []int64 { return mapCompare[T](equal)(x, y) } func notEqualSlice[T number](x, y []T) []int64 { return mapCompare[T](notEqual)(x, y) } func isNaNSlice[T float](x []T) []int64 { return map1[T](func(x T) int64 { if isNaN(x) { return -1 } return 0 })(x) } func ceilSlice[T float](x []T) []T { return map1[T](ceil)(x) } func floorSlice[T float](x []T) []T { return map1[T](floor)(x) } func notSlice[T integer](x []T) []T { return map1[T](not)(x) } func roundSlice[T float](x []T) []T { return map1[T](round)(x) } func sqrtSlice[T float](x []T) []T { return map1[T](sqrt)(x) } func truncSlice[T float](x []T) []T { return map1[T](trunc)(x) } func imaSlice[T integer](x, y, z []T) []T { return map3[T](ima)(x, y, z) } func fmaSlice[T float](x, y, z []T) []T { return map3[T](fma)(x, y, z) } func satToInt8[T integer](x T) int8 { var m int8 = -128 var M int8 = 127 if T(M) < T(m) { // expecting T being a larger type panic("bad input type") } if x < T(m) { return m } if x > T(M) { return M } return int8(x) } func satToUint8[T integer](x T) uint8 { var M uint8 = 255 if T(M) < 0 { // expecting T being a larger type panic("bad input type") } if x < 0 { return 0 } if x > T(M) { return M } return uint8(x) } func satToInt16[T integer](x T) int16 { var m int16 = -32768 var M int16 = 32767 if T(M) < T(m) { // expecting T being a larger type panic("bad input type") } if x < T(m) { return m } if x > T(M) { return M } return int16(x) } func satToUint16[T integer](x T) uint16 { var M uint16 = 65535 if T(M) < 0 { // expecting T being a larger type panic("bad input type") } if x < 0 { return 0 } if x > T(M) { return M } return uint16(x) } func satToInt32[T integer](x T) int32 { var m int32 = -1 << 31 var M int32 = 1<<31 - 1 if T(M) < T(m) { // expecting T being a larger type panic("bad input type") } if x < T(m) { return m } if x > T(M) { return M } return int32(x) } func satToUint32[T integer](x T) uint32 { var M uint32 = 1<<32 - 1 if T(M) < 0 { // expecting T being a larger type panic("bad input type") } if x < 0 { return 0 } if x > T(M) { return M } return uint32(x) }