From 09ff25e3508287970940645b97e4d88e92bb5407 Mon Sep 17 00:00:00 2001 From: David Chase Date: Tue, 22 Jul 2025 16:39:42 -0400 Subject: [PATCH] [dev.simd] simd: add tests for simd conversions to Int32/Uint32. Change-Id: I71a6c6708e19d210f1fbdc72379f8215356ff02e Reviewed-on: https://go-review.googlesource.com/c/go/+/689718 Reviewed-by: Junyang Shao LUCI-TryBot-Result: Go LUCI --- src/simd/genfiles.go | 22 ++++++-- src/simd/simulation_helpers_test.go | 28 ++++++++++ src/simd/unary_helpers_test.go | 84 +++++++++++++++++++++++++++++ src/simd/unary_test.go | 14 +++++ 4 files changed, 145 insertions(+), 3 deletions(-) diff --git a/src/simd/genfiles.go b/src/simd/genfiles.go index 7106db2d31..76f16392e6 100644 --- a/src/simd/genfiles.go +++ b/src/simd/genfiles.go @@ -43,7 +43,7 @@ var allShapes = &shapes{ // these are the shapes that are currently converted to int32 // (not all conversions are available, yet) -var toInt32Shapes = &shapes{ +var convert32Shapes = &shapes{ vecs: []int{128, 256, 512}, floats: []int{32}, } @@ -187,7 +187,7 @@ func test{{.Vec}}Unary(t *testing.T, f func(_ simd.{{.Vec}}) simd.{{.Vec}}, want } `) -var unaryTemplateToInt32 = shapedTemplateOf(toInt32Shapes, "unary_int32_helpers", ` +var unaryTemplateToInt32 = shapedTemplateOf(convert32Shapes, "unary_int32_helpers", ` // test{{.Vec}}Unary tests the simd unary method f against the expected behavior generated by want func test{{.Vec}}UnaryToInt32(t *testing.T, f func(x simd.{{.Vec}}) simd.Int32x{{.Count}}, want func(x []{{.Type}}) []int32) { n := {{.Count}} @@ -203,6 +203,22 @@ func test{{.Vec}}UnaryToInt32(t *testing.T, f func(x simd.{{.Vec}}) simd.Int32x{ } `) +var unaryTemplateToUint32 = shapedTemplateOf(convert32Shapes, "unary_uint32_helpers", ` +// test{{.Vec}}Unary tests the simd unary method f against the expected behavior generated by want +func test{{.Vec}}UnaryToUint32(t *testing.T, f func(x simd.{{.Vec}}) simd.Uint32x{{.Count}}, want func(x []{{.Type}}) []uint32) { + n := {{.Count}} + t.Helper() + forSlice(t, {{.Type}}s, n, func(x []{{.Type}}) bool { + t.Helper() + a := simd.Load{{.Vec}}Slice(x) + g := make([]uint32, n) + f(a).StoreSlice(g) + w := want(x) + return checkSlicesLogInput(t, g, w, func() {t.Helper(); t.Logf("x=%v", x)}) + }) +} +`) + var binaryTemplate = templateOf("binary_helpers", ` // test{{.Vec}}Binary tests the simd binary method f against the expected behavior generated by want func test{{.Vec}}Binary(t *testing.T, f func(_, _ simd.{{.Vec}}) simd.{{.Vec}}, want func(_, _ []{{.Type}}) []{{.Type}}) { @@ -295,7 +311,7 @@ func main() { one(*sl, prologue, sliceTemplate) } if *uh != "" { - one(*uh, curryTestPrologue("unary simd methods"), unaryTemplate) + one(*uh, curryTestPrologue("unary simd methods"), unaryTemplate, unaryTemplateToInt32, unaryTemplateToUint32) } if *bh != "" { one(*bh, curryTestPrologue("binary simd methods"), binaryTemplate) diff --git a/src/simd/simulation_helpers_test.go b/src/simd/simulation_helpers_test.go index 1def39cd92..ec3d795249 100644 --- a/src/simd/simulation_helpers_test.go +++ b/src/simd/simulation_helpers_test.go @@ -106,6 +106,26 @@ func fma[T float](x, y, z T) T { return T(math.FMA(float64(x), float64(y), float64(z))) } +func toInt32[T number](x T) int32 { + return int32(x) +} + +func toUint32[T number](x T) uint32 { + switch y := (any(x)).(type) { + case float32: + if y < 0 || y > float32(math.MaxUint32) || y != y { + return math.MaxUint32 + } + case float64: + if y < 0 || y > float64(math.MaxUint32) || y != y { + return math.MaxUint32 + } + } + return uint32(x) +} + +// Slice versions of all these elementwise operations + func addSlice[T number](x, y []T) []T { return map2[T](add)(x, y) } @@ -202,3 +222,11 @@ func imaSlice[T integer](x, y, z []T) []T { func fmaSlice[T float](x, y, z []T) []T { return map3[T](fma)(x, y, z) } + +func toInt32Slice[T number](x []T) []int32 { + return map1[T](toInt32)(x) +} + +func toUint32Slice[T number](x []T) []uint32 { + return map1[T](toUint32)(x) +} diff --git a/src/simd/unary_helpers_test.go b/src/simd/unary_helpers_test.go index cdc5151a21..4e0f09428e 100644 --- a/src/simd/unary_helpers_test.go +++ b/src/simd/unary_helpers_test.go @@ -432,3 +432,87 @@ func testFloat64x8Unary(t *testing.T, f func(_ simd.Float64x8) simd.Float64x8, w return checkSlicesLogInput(t, g, w, func() { t.Helper(); t.Logf("x=%v", x) }) }) } + +// testFloat32x4Unary tests the simd unary method f against the expected behavior generated by want +func testFloat32x4UnaryToInt32(t *testing.T, f func(x simd.Float32x4) simd.Int32x4, want func(x []float32) []int32) { + n := 4 + t.Helper() + forSlice(t, float32s, n, func(x []float32) bool { + t.Helper() + a := simd.LoadFloat32x4Slice(x) + g := make([]int32, n) + f(a).StoreSlice(g) + w := want(x) + return checkSlicesLogInput(t, g, w, func() { t.Helper(); t.Logf("x=%v", x) }) + }) +} + +// testFloat32x8Unary tests the simd unary method f against the expected behavior generated by want +func testFloat32x8UnaryToInt32(t *testing.T, f func(x simd.Float32x8) simd.Int32x8, want func(x []float32) []int32) { + n := 8 + t.Helper() + forSlice(t, float32s, n, func(x []float32) bool { + t.Helper() + a := simd.LoadFloat32x8Slice(x) + g := make([]int32, n) + f(a).StoreSlice(g) + w := want(x) + return checkSlicesLogInput(t, g, w, func() { t.Helper(); t.Logf("x=%v", x) }) + }) +} + +// testFloat32x16Unary tests the simd unary method f against the expected behavior generated by want +func testFloat32x16UnaryToInt32(t *testing.T, f func(x simd.Float32x16) simd.Int32x16, want func(x []float32) []int32) { + n := 16 + t.Helper() + forSlice(t, float32s, n, func(x []float32) bool { + t.Helper() + a := simd.LoadFloat32x16Slice(x) + g := make([]int32, n) + f(a).StoreSlice(g) + w := want(x) + return checkSlicesLogInput(t, g, w, func() { t.Helper(); t.Logf("x=%v", x) }) + }) +} + +// testFloat32x4Unary tests the simd unary method f against the expected behavior generated by want +func testFloat32x4UnaryToUint32(t *testing.T, f func(x simd.Float32x4) simd.Uint32x4, want func(x []float32) []uint32) { + n := 4 + t.Helper() + forSlice(t, float32s, n, func(x []float32) bool { + t.Helper() + a := simd.LoadFloat32x4Slice(x) + g := make([]uint32, n) + f(a).StoreSlice(g) + w := want(x) + return checkSlicesLogInput(t, g, w, func() { t.Helper(); t.Logf("x=%v", x) }) + }) +} + +// testFloat32x8Unary tests the simd unary method f against the expected behavior generated by want +func testFloat32x8UnaryToUint32(t *testing.T, f func(x simd.Float32x8) simd.Uint32x8, want func(x []float32) []uint32) { + n := 8 + t.Helper() + forSlice(t, float32s, n, func(x []float32) bool { + t.Helper() + a := simd.LoadFloat32x8Slice(x) + g := make([]uint32, n) + f(a).StoreSlice(g) + w := want(x) + return checkSlicesLogInput(t, g, w, func() { t.Helper(); t.Logf("x=%v", x) }) + }) +} + +// testFloat32x16Unary tests the simd unary method f against the expected behavior generated by want +func testFloat32x16UnaryToUint32(t *testing.T, f func(x simd.Float32x16) simd.Uint32x16, want func(x []float32) []uint32) { + n := 16 + t.Helper() + forSlice(t, float32s, n, func(x []float32) bool { + t.Helper() + a := simd.LoadFloat32x16Slice(x) + g := make([]uint32, n) + f(a).StoreSlice(g) + w := want(x) + return checkSlicesLogInput(t, g, w, func() { t.Helper(); t.Logf("x=%v", x) }) + }) +} diff --git a/src/simd/unary_test.go b/src/simd/unary_test.go index be6a0909be..6565df3096 100644 --- a/src/simd/unary_test.go +++ b/src/simd/unary_test.go @@ -82,3 +82,17 @@ func TestAbsolute(t *testing.T) { testInt64x8Unary(t, simd.Int64x8.Absolute, map1[int64](abs)) } } + +func TestToInt32(t *testing.T) { + testFloat32x4UnaryToInt32(t, simd.Float32x4.ConvertToInt32, toInt32Slice[float32]) + testFloat32x8UnaryToInt32(t, simd.Float32x8.ConvertToInt32, toInt32Slice[float32]) +} + +func TestToUint32(t *testing.T) { + if !simd.HasAVX512() { + t.Skip("Needs AVX512") + } + testFloat32x4UnaryToUint32(t, simd.Float32x4.ConvertToUint32, toUint32Slice[float32]) + testFloat32x8UnaryToUint32(t, simd.Float32x8.ConvertToUint32, toUint32Slice[float32]) + testFloat32x16UnaryToUint32(t, simd.Float32x16.ConvertToUint32, toUint32Slice[float32]) +} -- 2.52.0