]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: fix slice bounds check elimination after function inlining
authorYoulin Feng <fengyoulin@live.com>
Sat, 31 Jan 2026 03:23:41 +0000 (11:23 +0800)
committerGopher Robot <gobot@golang.org>
Thu, 12 Feb 2026 17:29:09 +0000 (09:29 -0800)
When creating a dynamically-sized slice, the compiler attempts to use a
stack-allocated buffer if the slice does not escape and its buffer size
is ≤ 32 bytes.

In this case, the SSA will contain a (OpPhi (OpSliceMake) (OpSliceMake))
value: one OpSliceMake uses the stack-allocated buffer, and the other
uses the heap-allocated buffer. The len and cap arguments for these two
OpSliceMake values are expected to be identical.

This CL enables the prove pass to recognize this scenario and handle
OpSliceLen and OpSliceCap as intended.

Fixes #77375

Change-Id: Id77a2473caf66d366f5c94108aa6cb6d3df5b887
Reviewed-on: https://go-review.googlesource.com/c/go/+/740840
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
src/cmd/compile/internal/ssa/_gen/generic.rules
src/cmd/compile/internal/ssa/prove.go
src/cmd/compile/internal/ssa/rewritegeneric.go
test/codegen/issue77375.go [new file with mode: 0644]

index b34fee095e669af0d9c5b5922da4c5fdba2c9649..495a8d9dbf9505a187a10b8bd702bb83390d1437 100644 (file)
     (ConstNil <v.Type.Elem().PtrTo()>)
     (Const64 <typ.Int> [0])
     (Const64 <typ.Int> [0]))
+(SliceLen (Phi (SliceMake _ x _) (SliceMake _ x _))) => x
+(SliceCap (Phi (SliceMake _ _ x) (SliceMake _ _ x))) => x
 
 // Special rule to help constant slicing; len > 0 implies cap > 0 implies Slicemask is all 1
 (SliceMake (AddPtr <t> x (And64 y (Slicemask _))) w:(Const64 [c]) z) && c > 0 => (SliceMake (AddPtr <t> x y) w z)
index 0bc97db73c44f4d387752cd0cf0cbbb881341c53..27234b4f9332e9437331b1d9a581ad7bbdf5cc2b 100644 (file)
@@ -3063,7 +3063,7 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
 
 func removeBranch(b *Block, branch branch) {
        c := b.Controls[0]
-       if b.Func.pass.debug > 0 {
+       if c != nil && b.Func.pass.debug > 0 {
                verb := "Proved"
                if branch == positive {
                        verb = "Disproved"
index ee74d7c9714f02ddd76d519d19a7ff1363f18c14..4e8f7039d753db46c5e4fa11014f986a75f74a5a 100644 (file)
@@ -30705,6 +30705,29 @@ func rewriteValuegeneric_OpSliceCap(v *Value) bool {
                v.AddArg(x)
                return true
        }
+       // match: (SliceCap (Phi (SliceMake _ _ x) (SliceMake _ _ x)))
+       // result: x
+       for {
+               if v_0.Op != OpPhi || len(v_0.Args) != 2 {
+                       break
+               }
+               _ = v_0.Args[1]
+               v_0_0 := v_0.Args[0]
+               if v_0_0.Op != OpSliceMake {
+                       break
+               }
+               x := v_0_0.Args[2]
+               v_0_1 := v_0.Args[1]
+               if v_0_1.Op != OpSliceMake {
+                       break
+               }
+               _ = v_0_1.Args[2]
+               if x != v_0_1.Args[2] {
+                       break
+               }
+               v.copyOf(x)
+               return true
+       }
        return false
 }
 func rewriteValuegeneric_OpSliceLen(v *Value) bool {
@@ -30761,6 +30784,29 @@ func rewriteValuegeneric_OpSliceLen(v *Value) bool {
                v.AddArg(x)
                return true
        }
+       // match: (SliceLen (Phi (SliceMake _ x _) (SliceMake _ x _)))
+       // result: x
+       for {
+               if v_0.Op != OpPhi || len(v_0.Args) != 2 {
+                       break
+               }
+               _ = v_0.Args[1]
+               v_0_0 := v_0.Args[0]
+               if v_0_0.Op != OpSliceMake {
+                       break
+               }
+               x := v_0_0.Args[1]
+               v_0_1 := v_0.Args[1]
+               if v_0_1.Op != OpSliceMake {
+                       break
+               }
+               _ = v_0_1.Args[1]
+               if x != v_0_1.Args[1] {
+                       break
+               }
+               v.copyOf(x)
+               return true
+       }
        // match: (SliceLen (SelectN [0] (StaticLECall {sym} _ newLen:(Const64) _ _ _ _)))
        // cond: (isSameCall(sym, "runtime.growslice") || isSameCall(sym, "runtime.growsliceNoAlias"))
        // result: newLen
diff --git a/test/codegen/issue77375.go b/test/codegen/issue77375.go
new file mode 100644 (file)
index 0000000..f74ce39
--- /dev/null
@@ -0,0 +1,39 @@
+// asmcheck
+
+// 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.
+
+package codegen
+
+func Range(n int) []int {
+       m := make([]int, n)
+
+       for i := 0; i < n; i++ {
+               m[i] = i
+       }
+
+       for i := range n {
+               m[i] = i
+       }
+
+       for i := range len(m) {
+               m[i] = i
+       }
+
+       for i := range m {
+               m[i] = i
+       }
+
+       return m
+}
+
+func F(size int) {
+       // amd64:-`.*panicBounds`
+       // arm64:-`.*panicBounds`
+       Range(size)
+}
+
+func main() {
+       F(-1)
+}