]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.24] cmd/compile: use inline-Pos-based recursion test
authorDavid Chase <drchase@google.com>
Wed, 5 Mar 2025 18:44:12 +0000 (13:44 -0500)
committerDavid Chase <drchase@google.com>
Wed, 12 Mar 2025 20:50:58 +0000 (13:50 -0700)
Look at the inlining stack of positions for a call site,
if the line/col/file of the call site appears in that
stack, do not inline.  This subsumes all the other
recently-added recursive inlining checks, but they are
left in to make this easier+safer to backport.

Fixes #72822

Change-Id: I0f487bb0d4c514015907c649312672b7be464abd
Reviewed-on: https://go-review.googlesource.com/c/go/+/655155
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
(cherry picked from commit cad4dca518a3a984bfd6b19ee304a59f51937fd8)
Reviewed-on: https://go-review.googlesource.com/c/go/+/657075

src/cmd/compile/internal/inline/inl.go
test/fixedbugs/issue72090.go [new file with mode: 0644]

index f17af5283a5520765765a9b3d933878dbecd890a..bddf4aa249c28b14898d5ef6ddd9ba9918a96a91 100644 (file)
@@ -42,6 +42,7 @@ import (
        "cmd/compile/internal/types"
        "cmd/internal/obj"
        "cmd/internal/pgo"
+       "cmd/internal/src"
 )
 
 // Inlining budget parameters, gathered in one place
@@ -974,6 +975,16 @@ func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller, closureCal
        return true, 0, metric, hot
 }
 
+// parsePos returns all the inlining positions and the innermost position.
+func parsePos(pos src.XPos, posTmp []src.Pos) ([]src.Pos, src.Pos) {
+       ctxt := base.Ctxt
+       ctxt.AllPos(pos, func(p src.Pos) {
+               posTmp = append(posTmp, p)
+       })
+       l := len(posTmp) - 1
+       return posTmp[:l], posTmp[l]
+}
+
 // canInlineCallExpr returns true if the call n from caller to callee
 // can be inlined, plus the score computed for the call expr in question,
 // and whether the callee is hot according to PGO.
@@ -1001,6 +1012,17 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa
                return false, 0, false
        }
 
+       callees, calleeInner := parsePos(n.Pos(), make([]src.Pos, 0, 10))
+
+       for _, p := range callees {
+               if p.Line() == calleeInner.Line() && p.Col() == calleeInner.Col() && p.AbsFilename() == calleeInner.AbsFilename() {
+                       if log && logopt.Enabled() {
+                               logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(callerfn)))
+                       }
+                       return false, 0, false
+               }
+       }
+
        if callee == callerfn {
                // Can't recursively inline a function into itself.
                if log && logopt.Enabled() {
diff --git a/test/fixedbugs/issue72090.go b/test/fixedbugs/issue72090.go
new file mode 100644 (file)
index 0000000..ca8dc34
--- /dev/null
@@ -0,0 +1,85 @@
+// build
+
+// 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.
+
+package main
+
+import (
+       "iter"
+)
+
+type leafSet map[rune]struct{}
+
+type branchMap map[rune]*node
+
+func (bm branchMap) findOrCreateBranch(r rune) *node {
+       if _, ok := bm[r]; !ok {
+               bm[r] = newNode()
+       }
+       return bm[r]
+}
+
+func (bm branchMap) allSuffixes() iter.Seq[string] {
+       return func(yield func(string) bool) {
+               for r, n := range bm {
+                       for s := range n.allStrings() {
+                               if !yield(string(r) + s) {
+                                       return
+                               }
+                       }
+               }
+       }
+}
+
+type node struct {
+       leafSet
+       branchMap
+}
+
+func newNode() *node {
+       return &node{make(leafSet), make(branchMap)}
+}
+
+func (n *node) add(s []rune) {
+       switch len(s) {
+       case 0:
+               return
+       case 1:
+               n.leafSet[s[0]] = struct{}{}
+       default:
+               n.branchMap.findOrCreateBranch(s[0]).add(s[1:])
+       }
+}
+
+func (n *node) addString(s string) {
+       n.add([]rune(s))
+}
+
+func (n *node) allStrings() iter.Seq[string] {
+       return func(yield func(string) bool) {
+               for s := range n.leafSet {
+                       if !yield(string(s)) {
+                               return
+                       }
+               }
+               for r, n := range n.branchMap {
+                       for s := range n.allSuffixes() {
+                               if !yield(string(r) + s) {
+                                       return
+                               }
+                       }
+               }
+       }
+}
+
+func main() {
+       root := newNode()
+       for _, s := range []string{"foo", "bar", "baz", "a", "b", "c", "hello", "world"} {
+               root.addString(s)
+       }
+       for s := range root.allStrings() {
+               println(s)
+       }
+}