]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/ssa: eliminate string copies for calls to unique.Make
authorJake Bailey <jacob.b.bailey@gmail.com>
Tue, 13 May 2025 03:39:54 +0000 (20:39 -0700)
committerCherry Mui <cherryyz@google.com>
Thu, 22 May 2025 03:20:31 +0000 (20:20 -0700)
unique.Make always copies strings passed into it, so it's safe to not
copy byte slices converted to strings either. Handle this just like map
accesses with string(b) as keys.

This CL only handles unique.Make(string(b)), not nested cases like
unique.Make([2]string{string(b1), string(b2)}); this could be done in a
followup CL but the map lookup code in walk is sufficiently different
than the call handling code that I didn't attempt it. (SSA is much
easier).

Fixes #71926

Change-Id: Ic2f82f2f91963d563b4ddb1282bd49fc40da8b85
Reviewed-on: https://go-review.googlesource.com/c/go/+/672135
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/compile/internal/ssa/_gen/generic.rules
src/cmd/compile/internal/ssa/rewritegeneric.go
src/unique/handle_test.go
test/codegen/unique.go [new file with mode: 0644]

index baa26133fe05351a1a3489a7090e1d203782c653..b178a1add6de29eb2181106a311dd7ac0fd0a099 100644 (file)
   && clobber(sbts)
   && clobber(key)
 => (StaticLECall {f} [argsize] typ_ map_ (StringMake <typ.String> ptr len) mem)
+
+// Similarly to map lookups, also handle unique.Make for strings, which unique.Make will clone.
+(StaticLECall {f} [argsize] dict_ key:(SelectN [0] sbts:(StaticLECall {g} _ ptr len mem)) m:(SelectN [1] sbts))
+  && isSameCall(f, "unique.Make[go.shape.string]")
+  && isSameCall(g, "runtime.slicebytetostring")
+  && key.Uses == 1
+  && sbts.Uses == 2
+  && resetCopy(m, mem)
+  && clobber(sbts)
+  && clobber(key)
+=> (StaticLECall {f} [argsize] dict_ (StringMake <typ.String> ptr len) mem)
index b8866cc56282e3edda198f6f1ee3fb953733594a..bfbd3c8522ed241f91b4d2489546bc89c486b905 100644 (file)
@@ -30743,6 +30743,41 @@ func rewriteValuegeneric_OpStaticLECall(v *Value) bool {
                v.AddArg4(typ_, map_, v0, mem)
                return true
        }
+       // match: (StaticLECall {f} [argsize] dict_ key:(SelectN [0] sbts:(StaticLECall {g} _ ptr len mem)) m:(SelectN [1] sbts))
+       // cond: isSameCall(f, "unique.Make[go.shape.string]") && isSameCall(g, "runtime.slicebytetostring") && key.Uses == 1 && sbts.Uses == 2 && resetCopy(m, mem) && clobber(sbts) && clobber(key)
+       // result: (StaticLECall {f} [argsize] dict_ (StringMake <typ.String> ptr len) mem)
+       for {
+               if len(v.Args) != 3 {
+                       break
+               }
+               argsize := auxIntToInt32(v.AuxInt)
+               f := auxToCall(v.Aux)
+               _ = v.Args[2]
+               dict_ := v.Args[0]
+               key := v.Args[1]
+               if key.Op != OpSelectN || auxIntToInt64(key.AuxInt) != 0 {
+                       break
+               }
+               sbts := key.Args[0]
+               if sbts.Op != OpStaticLECall || len(sbts.Args) != 4 {
+                       break
+               }
+               g := auxToCall(sbts.Aux)
+               mem := sbts.Args[3]
+               ptr := sbts.Args[1]
+               len := sbts.Args[2]
+               m := v.Args[2]
+               if m.Op != OpSelectN || auxIntToInt64(m.AuxInt) != 1 || sbts != m.Args[0] || !(isSameCall(f, "unique.Make[go.shape.string]") && isSameCall(g, "runtime.slicebytetostring") && key.Uses == 1 && sbts.Uses == 2 && resetCopy(m, mem) && clobber(sbts) && clobber(key)) {
+                       break
+               }
+               v.reset(OpStaticLECall)
+               v.AuxInt = int32ToAuxInt(argsize)
+               v.Aux = callToAux(f)
+               v0 := b.NewValue0(v.Pos, OpStringMake, typ.String)
+               v0.AddArg2(ptr, len)
+               v.AddArg3(dict_, v0, mem)
+               return true
+       }
        return false
 }
 func rewriteValuegeneric_OpStore(v *Value) bool {
index 5c42cb494cf1c243fe0debdea8039022289b39da..4c7f1f9752e4d276b1b81f2fcf34e20efcf641d3 100644 (file)
@@ -233,7 +233,7 @@ func TestMakeAllocs(t *testing.T) {
                        stringHandle = Make(string(b[:]))
                }},
 
-               {name: "bytes", allocs: 1, f: func() {
+               {name: "bytes", allocs: 0, f: func() {
                        stringHandle = Make(string(heapBytes))
                }},
 
@@ -241,7 +241,7 @@ func TestMakeAllocs(t *testing.T) {
                        stringHandle = Make(string(heapBytes[:16]))
                }},
 
-               {name: "bytes truncated long", allocs: 1, f: func() {
+               {name: "bytes truncated long", allocs: 0, f: func() {
                        stringHandle = Make(string(heapBytes[:40]))
                }},
 
diff --git a/test/codegen/unique.go b/test/codegen/unique.go
new file mode 100644 (file)
index 0000000..8ddc986
--- /dev/null
@@ -0,0 +1,24 @@
+// asmcheck
+
+// Copyright 2015 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.
+
+package codegen
+
+import "unique"
+
+func BytesToHandle(b []byte) unique.Handle[string] {
+       // amd64:-`.*runtime\.slicebytetostring\(`
+       return unique.Make(string(b))
+}
+
+type Pair struct {
+       S1 string
+       S2 string
+}
+
+func BytesPairToHandle(b1, b2 []byte) unique.Handle[Pair] {
+       // TODO: should not copy b1 and b2.
+       return unique.Make(Pair{string(b1), string(b2)})
+}