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")
+	}
+}