]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: handle loops better during stack allocation of slices
authorKeith Randall <khr@golang.org>
Thu, 20 Nov 2025 17:42:16 +0000 (09:42 -0800)
committerKeith Randall <khr@golang.org>
Wed, 26 Nov 2025 21:33:51 +0000 (13:33 -0800)
Don't use the move2heap optimization if the move2heap is inside
a loop deeper than the declaration of the slice. We really only want
to do the move2heap operation once.

Change-Id: I4a68d01609c2c9d4e0abe4580839e70059393a81
Reviewed-on: https://go-review.googlesource.com/c/go/+/722440
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/compile/internal/slice/slice.go
test/codegen/append.go

index 7a32e7adbd2fcc30ab85cf0b9391fbf34bdfa677..17eba5b77265612c7847301202d827c97121579f 100644 (file)
@@ -152,6 +152,11 @@ func analyze(fn *ir.Func) {
                // least weight 2. (Note: appends in loops have weight >= 2.)
                appendWeight int
 
+               // Loop depth at declaration point.
+               // Use for heuristics only, it is not guaranteed to be correct
+               // in the presence of gotos.
+               declDepth int
+
                // Whether we ever do cap(s), or other operations that use cap(s)
                // (possibly implicitly), like s[i:j].
                capUsed bool
@@ -209,6 +214,20 @@ func analyze(fn *ir.Func) {
                        i.s.Opt = nil
                        return
                }
+               if loopDepth > i.declDepth {
+                       // Conservatively, we disable this optimization when the
+                       // transition is inside a loop. This can result in adding
+                       // overhead unnecessarily in cases like:
+                       // func f(n int, p *[]byte) {
+                       //     var s []byte
+                       //     for i := range n {
+                       //         *p = s
+                       //         s = append(s, 0)
+                       //     }
+                       // }
+                       i.s.Opt = nil
+                       return
+               }
                i.transition = loc
        }
 
@@ -237,7 +256,7 @@ func analyze(fn *ir.Func) {
                                        // s = append(s, ...) is ok
                                        i.okUses += 2
                                        i.appends = append(i.appends, y)
-                                       i.appendWeight += 1 + loopDepth
+                                       i.appendWeight += 1 + (loopDepth - i.declDepth)
                                }
                                // TODO: s = append(nil, ...)?
                        }
@@ -277,6 +296,7 @@ func analyze(fn *ir.Func) {
                        n := n.(*ir.Decl)
                        if i := tracking(n.X); i != nil {
                                i.okUses++
+                               i.declDepth = loopDepth
                        }
                case ir.OINDEX:
                        n := n.(*ir.IndexExpr)
index 0e58a48c458fd60bccdf46db1457d3507c0386da..e90fa87ed2c8e5dd46c246d3dcc41e87a2e658f6 100644 (file)
@@ -185,6 +185,38 @@ func Append17(n int) []int {
        return r
 }
 
+func Append18(n int, p *[]int) {
+       var r []int
+       for i := range n {
+               // amd64:-`.*moveSliceNoCapNoScan`
+               *p = r
+               // amd64:`.*growslice`
+               r = append(r, i)
+       }
+}
+
+func Append19(n int, p [][]int) {
+       for j := range p {
+               var r []int
+               for i := range n {
+                       // amd64:`.*growslice`
+                       r = append(r, i)
+               }
+               // amd64:`.*moveSliceNoCapNoScan`
+               p[j] = r
+       }
+}
+
+func Append20(n int, p [][]int) {
+       for j := range p {
+               var r []int
+               // amd64:`.*growslice`
+               r = append(r, 0)
+               // amd64:-`.*moveSliceNoCapNoScan`
+               p[j] = r
+       }
+}
+
 //go:noinline
 func useSlice(s []int) {
 }