From: David Chase Date: Wed, 5 Mar 2025 18:44:12 +0000 (-0500) Subject: [release-branch.go1.24] cmd/compile: use inline-Pos-based recursion test X-Git-Tag: go1.24.2~15 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=c2a34bedeeaa0c06297dacc68d6a7f6a9918a346;p=gostls13.git [release-branch.go1.24] cmd/compile: use inline-Pos-based recursion test 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 Reviewed-by: Keith Randall LUCI-TryBot-Result: Go LUCI Reviewed-by: Cuong Manh Le (cherry picked from commit cad4dca518a3a984bfd6b19ee304a59f51937fd8) Reviewed-on: https://go-review.googlesource.com/c/go/+/657075 --- diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go index f17af5283a..bddf4aa249 100644 --- a/src/cmd/compile/internal/inline/inl.go +++ b/src/cmd/compile/internal/inline/inl.go @@ -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 index 0000000000..ca8dc34292 --- /dev/null +++ b/test/fixedbugs/issue72090.go @@ -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) + } +}