simd: repaired emulation String methods for simd they were a little and a lot broken added test Change-Id: I0769fd1ea65121efc21dc1629ab8e6a9e4c2d1a9 Reviewed-on: https://go-review.googlesource.com/c/go/+/795760 Auto-Submit: David Chase <drchase@google.com> TryBot-Bypass: David Chase <drchase@google.com> Reviewed-by: Junyang Shao <shaojunyang@google.com>
diff --git a/src/simd/internal/bridge/simd_emulated.go b/src/simd/internal/bridge/simd_emulated.go index 4a82d16..dea8c4d 100644 --- a/src/simd/internal/bridge/simd_emulated.go +++ b/src/simd/internal/bridge/simd_emulated.go
@@ -7,7 +7,7 @@ package bridge import ( - "fmt" + "internal/strconv" "math" "math/bits" ) @@ -32,6 +32,43 @@ return false } +type number interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 +} + +func sliceToString[T number](x []T) string { + s := "" + pfx := "{" + for _, y := range x { + s += pfx + pfx = "," + switch e := any(y).(type) { + case int8: + s += strconv.Itoa(int(e)) + case int16: + s += strconv.Itoa(int(e)) + case int32: + s += strconv.Itoa(int(e)) + case int64: + s += strconv.Itoa(int(e)) + case uint8: + s += strconv.FormatUint(uint64(e), 10) + case uint16: + s += strconv.FormatUint(uint64(e), 10) + case uint32: + s += strconv.FormatUint(uint64(e), 10) + case uint64: + s += strconv.FormatUint(uint64(e), 10) + case float32: + s += strconv.FormatFloat(float64(e), 'g', -1, 32) + case float64: + s += strconv.FormatFloat(e, 'g', -1, 64) + } + } + s += "}" + return s +} + type _simd struct { _ [0]func(*_simd) *_simd } @@ -302,7 +339,7 @@ for i := 0; i < 16; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -664,7 +701,7 @@ for i := 0; i < 8; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -1019,7 +1056,7 @@ for i := 0; i < 4; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -1259,7 +1296,7 @@ // String returns a string representation of the vector. func (x Int64s) String() string { - return fmt.Sprint([2]int64{int64(x.a), int64(x.b)}) + return sliceToString([]int64{int64(x.a), int64(x.b)}) } // Sub returns the element-wise difference of x and y. @@ -1500,7 +1537,7 @@ for i := 0; i < 16; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -1849,7 +1886,7 @@ for i := 0; i < 8; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -2175,7 +2212,7 @@ for i := 0; i < 4; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -2421,7 +2458,7 @@ // String returns a string representation of the vector. func (x Uint64s) String() string { - return fmt.Sprint([2]uint64{x.a, x.b}) + return sliceToString([]uint64{x.a, x.b}) } // Sub returns the element-wise difference of x and y. @@ -2736,7 +2773,7 @@ for i := 0; i < 4; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -3006,7 +3043,7 @@ // String returns a string representation of the vector. func (x Float64s) String() string { - return fmt.Sprint([2]float64{x.get(0), x.get(1)}) + return sliceToString([]float64{x.get(0), x.get(1)}) } // Sub returns the element-wise difference of x and y. @@ -3052,7 +3089,9 @@ // String returns a string representation of the vector. func (x Mask8s) String() string { - return fmt.Sprintf("{a:%#x, b:%#x}", x.a, x.b) + var s [16]int8 + x.ToInt8s().Neg().Store(s[:]) + return sliceToString(s[:]) } // ToInt8s converts the mask to an Int8s vector. @@ -3090,7 +3129,9 @@ // String returns a string representation of the vector. func (x Mask16s) String() string { - return fmt.Sprintf("{a:%#x, b:%#x}", x.a, x.b) + var s [8]int16 + x.ToInt16s().Neg().Store(s[:]) + return sliceToString(s[:]) } // ToInt16s converts the mask to an Int16s vector. @@ -3128,7 +3169,9 @@ // String returns a string representation of the vector. func (x Mask32s) String() string { - return fmt.Sprintf("{a:%#x, b:%#x}", x.a, x.b) + var s [4]int32 + x.ToInt32s().Neg().Store(s[:]) + return sliceToString(s[:]) } // ToInt32s converts the mask to an Int32s vector. @@ -3164,7 +3207,9 @@ // String returns a string representation of the vector. func (x Mask64s) String() string { - return fmt.Sprintf("{a:%#x, b:%#x}", x.a, x.b) + var s [2]int64 + x.ToInt64s().Neg().Store(s[:]) + return sliceToString(s[:]) } // ToInt64s converts the mask to an Int64s vector.
diff --git a/src/simd/simd_emulated.go b/src/simd/simd_emulated.go index d6d5607..ba87733 100644 --- a/src/simd/simd_emulated.go +++ b/src/simd/simd_emulated.go
@@ -7,7 +7,7 @@ package simd import ( - "fmt" + "internal/strconv" "math" "math/bits" ) @@ -32,6 +32,43 @@ return false } +type number interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 +} + +func sliceToString[T number](x []T) string { + s := "" + pfx := "{" + for _, y := range x { + s += pfx + pfx = "," + switch e := any(y).(type) { + case int8: + s += strconv.Itoa(int(e)) + case int16: + s += strconv.Itoa(int(e)) + case int32: + s += strconv.Itoa(int(e)) + case int64: + s += strconv.Itoa(int(e)) + case uint8: + s += strconv.FormatUint(uint64(e), 10) + case uint16: + s += strconv.FormatUint(uint64(e), 10) + case uint32: + s += strconv.FormatUint(uint64(e), 10) + case uint64: + s += strconv.FormatUint(uint64(e), 10) + case float32: + s += strconv.FormatFloat(float64(e), 'g', -1, 32) + case float64: + s += strconv.FormatFloat(e, 'g', -1, 64) + } + } + s += "}" + return s +} + // LoadInt8s loads a slice of int8 into an Int8s vector. func LoadInt8s(s []int8) Int8s { var a, b uint64 @@ -292,7 +329,7 @@ for i := 0; i < 16; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -648,7 +685,7 @@ for i := 0; i < 8; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -997,7 +1034,7 @@ for i := 0; i < 4; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -1231,7 +1268,7 @@ // String returns a string representation of the vector. func (x Int64s) String() string { - return fmt.Sprint([2]int64{int64(x.a), int64(x.b)}) + return sliceToString([]int64{int64(x.a), int64(x.b)}) } // Sub returns the element-wise difference of x and y. @@ -1466,7 +1503,7 @@ for i := 0; i < 16; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -1809,7 +1846,7 @@ for i := 0; i < 8; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -2129,7 +2166,7 @@ for i := 0; i < 4; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -2369,7 +2406,7 @@ // String returns a string representation of the vector. func (x Uint64s) String() string { - return fmt.Sprint([2]uint64{x.a, x.b}) + return sliceToString([]uint64{x.a, x.b}) } // Sub returns the element-wise difference of x and y. @@ -2677,7 +2714,7 @@ for i := 0; i < 4; i++ { parts[i] = x.get(i) } - return fmt.Sprint(parts) + return sliceToString(parts[:]) } // Sub returns the element-wise difference of x and y. @@ -2941,7 +2978,7 @@ // String returns a string representation of the vector. func (x Float64s) String() string { - return fmt.Sprint([2]float64{x.get(0), x.get(1)}) + return sliceToString([]float64{x.get(0), x.get(1)}) } // Sub returns the element-wise difference of x and y. @@ -2981,7 +3018,9 @@ // String returns a string representation of the vector. func (x Mask8s) String() string { - return fmt.Sprintf("{a:%#x, b:%#x}", x.a, x.b) + var s [16]int8 + x.ToInt8s().Neg().Store(s[:]) + return sliceToString(s[:]) } // ToInt8s converts the mask to an Int8s vector. @@ -3013,7 +3052,9 @@ // String returns a string representation of the vector. func (x Mask16s) String() string { - return fmt.Sprintf("{a:%#x, b:%#x}", x.a, x.b) + var s [8]int16 + x.ToInt16s().Neg().Store(s[:]) + return sliceToString(s[:]) } // ToInt16s converts the mask to an Int16s vector. @@ -3045,7 +3086,9 @@ // String returns a string representation of the vector. func (x Mask32s) String() string { - return fmt.Sprintf("{a:%#x, b:%#x}", x.a, x.b) + var s [4]int32 + x.ToInt32s().Neg().Store(s[:]) + return sliceToString(s[:]) } // ToInt32s converts the mask to an Int32s vector. @@ -3075,7 +3118,9 @@ // String returns a string representation of the vector. func (x Mask64s) String() string { - return fmt.Sprintf("{a:%#x, b:%#x}", x.a, x.b) + var s [2]int64 + x.ToInt64s().Neg().Store(s[:]) + return sliceToString(s[:]) } // ToInt64s converts the mask to an Int64s vector.
diff --git a/src/simd/testdata/tostring_test.go b/src/simd/testdata/tostring_test.go new file mode 100644 index 0000000..2e03c7a --- /dev/null +++ b/src/simd/testdata/tostring_test.go
@@ -0,0 +1,75 @@ +// Copyright 2026 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 + +package testdata_test + +import ( + "fmt" + "simd" + "testing" +) + +func fillUint8s(f func(i int) uint8) simd.Uint8s { + l := simd.Uint8s{}.Len() + x := make([]uint8, l, l) + for i := range l { + x[i] = f(i) + } + return simd.LoadUint8s(x) +} + +func fillFloat32s(f func(i int) float32) simd.Float32s { + l := simd.Float32s{}.Len() + x := make([]float32, l, l) + for i := range l { + x[i] = f(i) + } + return simd.LoadFloat32s(x) +} + +func stringFor(l int, f func(i int) int) string { + pfx := "{" + var want string + for i := range l { + want += pfx + pfx = "," + want += fmt.Sprintf("%d", f(i)) + } + want += "}" + return want +} + +func TestToString(t *testing.T) { + a := fillUint8s(func(i int) uint8 { return uint8(i) & 1 }) + b := fillUint8s(func(i int) uint8 { return uint8(i>>1) & 1 }) + m := a.Equal(b) + wantM := stringFor(a.Len(), + func(i int) int { + if i&1 == (i>>1)&1 { + return 1 + } + return 0 + }) + if got := m.String(); wantM != got { + t.Errorf("wantM=%s, got=%s", wantM, got) + } + wantA := stringFor(a.Len(), + func(i int) int { + return i & 1 + }) + if got := a.String(); wantA != got { + t.Errorf("wantA=%s, got=%s", wantA, got) + } + + f := fillFloat32s(func(i int) float32 { return float32(i) }) + wantF := stringFor(f.Len(), + func(i int) int { + return i + }) + if got := f.String(); wantF != got { + t.Errorf("wantF=%s, got=%s", wantF, got) + } +}
diff --git a/src/simd/testdata_test.go b/src/simd/testdata_test.go index 7a81b50..c485aae 100644 --- a/src/simd/testdata_test.go +++ b/src/simd/testdata_test.go
@@ -9,11 +9,12 @@ import ( "internal/testenv" "os" + "runtime" "strings" "testing" ) -func common(t *testing.T, dir, what, failWith string) { +func common(t *testing.T, dir, what, failWith string, moreEnv ...string) { t.Helper() t.Logf("subprocess test in testdata") testenv.MustHaveGoRun(t) @@ -32,6 +33,7 @@ goexp += "simd" } cmd.Env = append(cmd.Environ(), "GOEXPERIMENT="+goexp) + cmd.Env = append(cmd.Env, moreEnv...) if failWith == "" { cmd.Stdout = os.Stdout @@ -70,3 +72,10 @@ common(t, "testdata", "errors_test.go", "array length unsafe.Sizeof(v_from_simd) (value of type uintptr) must be constant") } + +func TestToString(t *testing.T) { + common(t, "testdata", "tostring_test.go", "") + if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" { + common(t, "testdata", "tostring_test.go", "", "GODEBUG=simd=0") + } +}