From c75965b666edf8399fc0c56ba0b94c2a4f5e7070 Mon Sep 17 00:00:00 2001 From: David Chase Date: Tue, 14 Oct 2025 12:25:07 -0400 Subject: [PATCH] [dev.simd] simd: added String() method to SIMD vectors. this required a little plumbing to get access to the "good" floating point formatting. Change-Id: Iebec157c28a39df59351bade53b09a3729fc49c0 Reviewed-on: https://go-review.googlesource.com/c/go/+/711781 Reviewed-by: Junyang Shao LUCI-TryBot-Result: Go LUCI --- src/go/build/deps_test.go | 8 +- src/internal/ftoa/ftoa.go | 23 +++ src/simd/genfiles.go | 11 ++ src/simd/internal/simd_test/simd_test.go | 29 ++++ src/simd/other_gen_amd64.go | 210 +++++++++++++++++++++++ src/simd/string.go | 49 ++++++ src/strconv/ftoa.go | 9 +- 7 files changed, 335 insertions(+), 4 deletions(-) create mode 100644 src/internal/ftoa/ftoa.go create mode 100644 src/simd/string.go diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index ec3c4bbdaa..93abfd394a 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -49,11 +49,13 @@ var depsRules = ` internal/coverage/uleb128, internal/coverage/calloc, internal/cpu, + internal/ftoa, internal/goarch, internal/godebugs, internal/goexperiment, internal/goos, internal/goversion, + internal/itoa, internal/nettrace, internal/platform, internal/profilerecord, @@ -70,7 +72,7 @@ var depsRules = ` internal/goarch < internal/abi; internal/byteorder, internal/cpu, internal/goarch < internal/chacha8rand; - internal/cpu < simd; + internal/cpu, internal/ftoa, internal/itoa < simd; # RUNTIME is the core runtime group of packages, all of them very light-weight. internal/abi, @@ -81,13 +83,13 @@ var depsRules = ` internal/godebugs, internal/goexperiment, internal/goos, + internal/itoa, internal/profilerecord, internal/trace/tracev2, math/bits, structs < internal/bytealg < internal/stringslite - < internal/itoa < internal/unsafeheader < internal/race < internal/msan @@ -175,7 +177,7 @@ var depsRules = ` MATH < runtime/metrics; - MATH, unicode/utf8 + MATH, unicode/utf8, internal/ftoa < strconv; unicode !< strconv; diff --git a/src/internal/ftoa/ftoa.go b/src/internal/ftoa/ftoa.go new file mode 100644 index 0000000000..678668c719 --- /dev/null +++ b/src/internal/ftoa/ftoa.go @@ -0,0 +1,23 @@ +// 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. + +// A hook to get correct floating point conversion from strconv +// in packages that cannot import strconv. + +package ftoa + +var formatFloatPtr func(f float64, fmt byte, prec, bitSize int) string + +func FormatFloat(f float64, fmt byte, prec, bitSize int) string { + if formatFloatPtr != nil { + return formatFloatPtr(f, fmt, prec, bitSize) + } + return "internal/ftoa.formatFloatPtr called before strconv.init()" +} + +func SetFormatFloat(ff func(f float64, fmt byte, prec, bitSize int) string) { + if formatFloatPtr == nil { + formatFloatPtr = ff + } +} diff --git a/src/simd/genfiles.go b/src/simd/genfiles.go index 7e904edb10..80234ac9f8 100644 --- a/src/simd/genfiles.go +++ b/src/simd/genfiles.go @@ -263,6 +263,7 @@ func unsafePrologue(s string, out io.Writer) { package simd import "unsafe" + `, s) } @@ -795,6 +796,15 @@ func (from {{.Base}}{{.WxC}}) ToMask() (to Mask{{.WxC}}) { } `) +var stringTemplate = shapedTemplateOf(allShapes, "String methods", ` +// String returns a string representation of SIMD vector x +func (x {{.VType}}) String() string { + var s [{{.Count}}]{{.Etype}} + x.Store(&s) + return sliceToString(s[:]) +} +`) + const TD = "internal/simd_test/" func main() { @@ -836,6 +846,7 @@ func main() { maskCvtTemplate, bitWiseIntTemplate, bitWiseUintTemplate, + stringTemplate, ) } if *ush != "" { diff --git a/src/simd/internal/simd_test/simd_test.go b/src/simd/internal/simd_test/simd_test.go index 422378eebe..295f7bf9ce 100644 --- a/src/simd/internal/simd_test/simd_test.go +++ b/src/simd/internal/simd_test/simd_test.go @@ -1001,3 +1001,32 @@ func TestSelect2FromPairConstGroupedInt512(t *testing.T) { foo(lh, 0, 3) foo(hl, 2, 1) } + +func TestString(t *testing.T) { + x := simd.LoadUint32x4Slice([]uint32{0, 1, 2, 3}) + y := simd.LoadInt64x4Slice([]int64{-4, -5, -6, -7}) + z := simd.LoadFloat32x4Slice([]float32{0.5, 1.5, -2.5, 3.5e9}) + w := simd.LoadFloat64x4Slice([]float64{0.5, 1.5, -2.5, 3.5e9}) + + sx := "{0,1,2,3}" + sy := "{-4,-5,-6,-7}" + sz := "{0.5,1.5,-2.5,3.5e+09}" + sw := sz + + if x.String() != sx { + t.Errorf("x=%s wanted %s", x, sx) + } + if y.String() != sy { + t.Errorf("y=%s wanted %s", y, sy) + } + if z.String() != sz { + t.Errorf("z=%s wanted %s", z, sz) + } + if w.String() != sw { + t.Errorf("w=%s wanted %s", w, sw) + } + t.Logf("w=%s", w) + t.Logf("x=%s", x) + t.Logf("y=%s", y) + t.Logf("z=%s", z) +} diff --git a/src/simd/other_gen_amd64.go b/src/simd/other_gen_amd64.go index 76fbe48b20..da11b227df 100644 --- a/src/simd/other_gen_amd64.go +++ b/src/simd/other_gen_amd64.go @@ -591,3 +591,213 @@ func (x Uint32x16) Not() Uint32x16 { func (x Uint64x8) Not() Uint64x8 { return x.Xor(x.Equal(x).AsInt64x8().AsUint64x8()) } + +// String returns a string representation of SIMD vector x +func (x Int8x16) String() string { + var s [16]int8 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Int16x8) String() string { + var s [8]int16 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Int32x4) String() string { + var s [4]int32 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Int64x2) String() string { + var s [2]int64 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint8x16) String() string { + var s [16]uint8 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint16x8) String() string { + var s [8]uint16 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint32x4) String() string { + var s [4]uint32 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint64x2) String() string { + var s [2]uint64 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Float32x4) String() string { + var s [4]float32 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Float64x2) String() string { + var s [2]float64 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Int8x32) String() string { + var s [32]int8 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Int16x16) String() string { + var s [16]int16 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Int32x8) String() string { + var s [8]int32 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Int64x4) String() string { + var s [4]int64 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint8x32) String() string { + var s [32]uint8 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint16x16) String() string { + var s [16]uint16 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint32x8) String() string { + var s [8]uint32 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint64x4) String() string { + var s [4]uint64 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Float32x8) String() string { + var s [8]float32 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Float64x4) String() string { + var s [4]float64 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Int8x64) String() string { + var s [64]int8 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Int16x32) String() string { + var s [32]int16 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Int32x16) String() string { + var s [16]int32 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Int64x8) String() string { + var s [8]int64 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint8x64) String() string { + var s [64]uint8 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint16x32) String() string { + var s [32]uint16 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint32x16) String() string { + var s [16]uint32 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Uint64x8) String() string { + var s [8]uint64 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Float32x16) String() string { + var s [16]float32 + x.Store(&s) + return sliceToString(s[:]) +} + +// String returns a string representation of SIMD vector x +func (x Float64x8) String() string { + var s [8]float64 + x.Store(&s) + return sliceToString(s[:]) +} diff --git a/src/simd/string.go b/src/simd/string.go new file mode 100644 index 0000000000..35584da021 --- /dev/null +++ b/src/simd/string.go @@ -0,0 +1,49 @@ +// 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 + +import ( + "internal/ftoa" + "internal/itoa" +) + +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 += itoa.Itoa(int(e)) + case int16: + s += itoa.Itoa(int(e)) + case int32: + s += itoa.Itoa(int(e)) + case int64: + s += itoa.Itoa(int(e)) + case uint8: + s += itoa.Uitoa(uint(e)) + case uint16: + s += itoa.Uitoa(uint(e)) + case uint32: + s += itoa.Uitoa(uint(e)) + case uint64: + s += itoa.Uitoa(uint(e)) + case float32: + s += ftoa.FormatFloat(float64(e), 'g', -1, 32) + case float64: + s += ftoa.FormatFloat(e, 'g', -1, 64) + } + } + s += "}" + return s +} diff --git a/src/strconv/ftoa.go b/src/strconv/ftoa.go index bfe26366e1..629df38240 100644 --- a/src/strconv/ftoa.go +++ b/src/strconv/ftoa.go @@ -10,7 +10,10 @@ package strconv -import "math" +import ( + "internal/ftoa" + "math" +) // TODO: move elsewhere? type floatInfo struct { @@ -22,6 +25,10 @@ type floatInfo struct { var float32info = floatInfo{23, 8, -127} var float64info = floatInfo{52, 11, -1023} +func init() { + ftoa.SetFormatFloat(FormatFloat) +} + // FormatFloat converts the floating-point number f to a string, // according to the format fmt and precision prec. It rounds the // result assuming that the original was obtained from a floating-point -- 2.52.0