Skip to content

Commit c2a34be

Browse files
committed
[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 <[email protected]> Reviewed-by: Keith Randall <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]> (cherry picked from commit cad4dca) Reviewed-on: https://go-review.googlesource.com/c/go/+/657075
1 parent 0ace2d8 commit c2a34be

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

src/cmd/compile/internal/inline/inl.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"cmd/compile/internal/types"
4343
"cmd/internal/obj"
4444
"cmd/internal/pgo"
45+
"cmd/internal/src"
4546
)
4647

4748
// Inlining budget parameters, gathered in one place
@@ -974,6 +975,16 @@ func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller, closureCal
974975
return true, 0, metric, hot
975976
}
976977

978+
// parsePos returns all the inlining positions and the innermost position.
979+
func parsePos(pos src.XPos, posTmp []src.Pos) ([]src.Pos, src.Pos) {
980+
ctxt := base.Ctxt
981+
ctxt.AllPos(pos, func(p src.Pos) {
982+
posTmp = append(posTmp, p)
983+
})
984+
l := len(posTmp) - 1
985+
return posTmp[:l], posTmp[l]
986+
}
987+
977988
// canInlineCallExpr returns true if the call n from caller to callee
978989
// can be inlined, plus the score computed for the call expr in question,
979990
// 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
10011012
return false, 0, false
10021013
}
10031014

1015+
callees, calleeInner := parsePos(n.Pos(), make([]src.Pos, 0, 10))
1016+
1017+
for _, p := range callees {
1018+
if p.Line() == calleeInner.Line() && p.Col() == calleeInner.Col() && p.AbsFilename() == calleeInner.AbsFilename() {
1019+
if log && logopt.Enabled() {
1020+
logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(callerfn)))
1021+
}
1022+
return false, 0, false
1023+
}
1024+
}
1025+
10041026
if callee == callerfn {
10051027
// Can't recursively inline a function into itself.
10061028
if log && logopt.Enabled() {

test/fixedbugs/issue72090.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// build
2+
3+
// Copyright 2025 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
import (
10+
"iter"
11+
)
12+
13+
type leafSet map[rune]struct{}
14+
15+
type branchMap map[rune]*node
16+
17+
func (bm branchMap) findOrCreateBranch(r rune) *node {
18+
if _, ok := bm[r]; !ok {
19+
bm[r] = newNode()
20+
}
21+
return bm[r]
22+
}
23+
24+
func (bm branchMap) allSuffixes() iter.Seq[string] {
25+
return func(yield func(string) bool) {
26+
for r, n := range bm {
27+
for s := range n.allStrings() {
28+
if !yield(string(r) + s) {
29+
return
30+
}
31+
}
32+
}
33+
}
34+
}
35+
36+
type node struct {
37+
leafSet
38+
branchMap
39+
}
40+
41+
func newNode() *node {
42+
return &node{make(leafSet), make(branchMap)}
43+
}
44+
45+
func (n *node) add(s []rune) {
46+
switch len(s) {
47+
case 0:
48+
return
49+
case 1:
50+
n.leafSet[s[0]] = struct{}{}
51+
default:
52+
n.branchMap.findOrCreateBranch(s[0]).add(s[1:])
53+
}
54+
}
55+
56+
func (n *node) addString(s string) {
57+
n.add([]rune(s))
58+
}
59+
60+
func (n *node) allStrings() iter.Seq[string] {
61+
return func(yield func(string) bool) {
62+
for s := range n.leafSet {
63+
if !yield(string(s)) {
64+
return
65+
}
66+
}
67+
for r, n := range n.branchMap {
68+
for s := range n.allSuffixes() {
69+
if !yield(string(r) + s) {
70+
return
71+
}
72+
}
73+
}
74+
}
75+
}
76+
77+
func main() {
78+
root := newNode()
79+
for _, s := range []string{"foo", "bar", "baz", "a", "b", "c", "hello", "world"} {
80+
root.addString(s)
81+
}
82+
for s := range root.allStrings() {
83+
println(s)
84+
}
85+
}

0 commit comments

Comments
 (0)