Skip to content

Commit 3fd9cb1

Browse files
committed
cmd/compile: fix bloop get name logic
This CL change getNameFrom impl to pattern match addressible patterns. Change-Id: If1faa22a3a012d501e911d8468a5702b348abf16 Reviewed-on: https://go-review.googlesource.com/c/go/+/724180 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com>
1 parent 3353c10 commit 3fd9cb1

File tree

2 files changed

+81
-52
lines changed

2 files changed

+81
-52
lines changed

‎src/cmd/compile/internal/bloop/bloop.go‎

Lines changed: 77 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -42,40 +42,42 @@ import (
4242
"cmd/compile/internal/reflectdata"
4343
"cmd/compile/internal/typecheck"
4444
"cmd/compile/internal/types"
45-
"fmt"
45+
"cmd/internal/src"
4646
)
4747

4848
// getNameFromNode tries to iteratively peel down the node to
4949
// get the name.
5050
func getNameFromNode(n ir.Node) *ir.Name {
51-
var ret *ir.Name
52-
if n.Op() == ir.ONAME {
53-
ret = n.(*ir.Name)
54-
} else {
55-
// avoid infinite recursion on circular referencing nodes.
56-
seen := map[ir.Node]bool{n: true}
57-
var findName func(ir.Node) bool
58-
findName = func(a ir.Node) bool {
59-
if a.Op() == ir.ONAME {
60-
ret = a.(*ir.Name)
61-
return true
62-
}
63-
if !seen[a] {
64-
seen[a] = true
65-
return ir.DoChildren(a, findName)
66-
}
67-
return false
51+
// Tries to iteratively peel down the node to get the names.
52+
for n != nil {
53+
switch n.Op() {
54+
case ir.ONAME:
55+
// Found the name, stop the loop.
56+
return n.(*ir.Name)
57+
case ir.OSLICE, ir.OSLICE3:
58+
n = n.(*ir.SliceExpr).X
59+
case ir.ODOT:
60+
n = n.(*ir.SelectorExpr).X
61+
case ir.OCONV, ir.OCONVIFACE, ir.OCONVNOP:
62+
n = n.(*ir.ConvExpr).X
63+
case ir.OADDR:
64+
n = n.(*ir.AddrExpr).X
65+
case ir.ODOTPTR:
66+
n = n.(*ir.SelectorExpr).X
67+
case ir.OINDEX, ir.OINDEXMAP:
68+
n = n.(*ir.IndexExpr).X
69+
default:
70+
n = nil
6871
}
69-
ir.DoChildren(n, findName)
7072
}
71-
return ret
73+
return nil
7274
}
7375

7476
// keepAliveAt returns a statement that is either curNode, or a
7577
// block containing curNode followed by a call to runtime.keepAlive for each
76-
// ONAME in ns. These calls ensure that names in ns will be live until
78+
// node in ns. These calls ensure that nodes in ns will be live until
7779
// after curNode's execution.
78-
func keepAliveAt(ns []*ir.Name, curNode ir.Node) ir.Node {
80+
func keepAliveAt(ns []ir.Node, curNode ir.Node) ir.Node {
7981
if len(ns) == 0 {
8082
return curNode
8183
}
@@ -109,12 +111,12 @@ func keepAliveAt(ns []*ir.Name, curNode ir.Node) ir.Node {
109111
return ir.NewBlockStmt(pos, calls)
110112
}
111113

112-
func debugName(name *ir.Name, line string) {
113-
if base.Flag.LowerM > 0 {
114+
func debugName(name *ir.Name, pos src.XPos) {
115+
if base.Flag.LowerM > 1 {
114116
if name.Linksym() != nil {
115-
fmt.Printf("%v: %s will be kept alive\n", line, name.Linksym().Name)
117+
base.WarnfAt(pos, "%s will be kept alive", name.Linksym().Name)
116118
} else {
117-
fmt.Printf("%v: expr will be kept alive\n", line)
119+
base.WarnfAt(pos, "expr will be kept alive")
118120
}
119121
}
120122
}
@@ -129,29 +131,50 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
129131
// Peel down struct and slice indexing to get the names
130132
name := getNameFromNode(n.X)
131133
if name != nil {
132-
debugName(name, ir.Line(stmt))
133-
ret = keepAliveAt([]*ir.Name{name}, n)
134+
debugName(name, n.Pos())
135+
ret = keepAliveAt([]ir.Node{name}, n)
136+
} else if deref := n.X.(*ir.StarExpr); deref != nil {
137+
ret = keepAliveAt([]ir.Node{deref}, n)
138+
if base.Flag.LowerM > 1 {
139+
base.WarnfAt(n.Pos(), "dereference will be kept alive")
140+
}
141+
} else if base.Flag.LowerM > 1 {
142+
base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
134143
}
135144
case *ir.AssignListStmt:
136-
names := []*ir.Name{}
145+
ns := []ir.Node{}
137146
for _, lhs := range n.Lhs {
138147
name := getNameFromNode(lhs)
139148
if name != nil {
140-
debugName(name, ir.Line(stmt))
141-
names = append(names, name)
149+
debugName(name, n.Pos())
150+
ns = append(ns, name)
151+
} else if deref := lhs.(*ir.StarExpr); deref != nil {
152+
ns = append(ns, deref)
153+
if base.Flag.LowerM > 1 {
154+
base.WarnfAt(n.Pos(), "dereference will be kept alive")
155+
}
156+
} else if base.Flag.LowerM > 1 {
157+
base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
142158
}
143159
}
144-
ret = keepAliveAt(names, n)
160+
ret = keepAliveAt(ns, n)
145161
case *ir.AssignOpStmt:
146162
name := getNameFromNode(n.X)
147163
if name != nil {
148-
debugName(name, ir.Line(stmt))
149-
ret = keepAliveAt([]*ir.Name{name}, n)
164+
debugName(name, n.Pos())
165+
ret = keepAliveAt([]ir.Node{name}, n)
166+
} else if deref := n.X.(*ir.StarExpr); deref != nil {
167+
ret = keepAliveAt([]ir.Node{deref}, n)
168+
if base.Flag.LowerM > 1 {
169+
base.WarnfAt(n.Pos(), "dereference will be kept alive")
170+
}
171+
} else if base.Flag.LowerM > 1 {
172+
base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
150173
}
151174
case *ir.CallExpr:
152-
names := []*ir.Name{}
153175
curNode := stmt
154176
if n.Fun != nil && n.Fun.Type() != nil && n.Fun.Type().NumResults() != 0 {
177+
ns := []ir.Node{}
155178
// This function's results are not assigned, assign them to
156179
// auto tmps and then keepAliveAt these autos.
157180
// Note: markStmt assumes the context that it's called - this CallExpr is
@@ -161,7 +184,7 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
161184
for i, res := range results {
162185
tmp := typecheck.TempAt(n.Pos(), curFn, res.Type)
163186
lhs[i] = tmp
164-
names = append(names, tmp)
187+
ns = append(ns, tmp)
165188
}
166189

167190
// Create an assignment statement.
@@ -174,33 +197,35 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
174197
if len(results) > 1 {
175198
plural = "s"
176199
}
177-
if base.Flag.LowerM > 0 {
178-
fmt.Printf("%v: function result%s will be kept alive\n", ir.Line(stmt), plural)
200+
if base.Flag.LowerM > 1 {
201+
base.WarnfAt(n.Pos(), "function result%s will be kept alive", plural)
179202
}
203+
ret = keepAliveAt(ns, curNode)
180204
} else {
181205
// This function probably doesn't return anything, keep its args alive.
182206
argTmps := []ir.Node{}
207+
names := []ir.Node{}
183208
for i, a := range n.Args {
184209
if name := getNameFromNode(a); name != nil {
185210
// If they are name, keep them alive directly.
186-
debugName(name, ir.Line(stmt))
211+
debugName(name, n.Pos())
187212
names = append(names, name)
188213
} else if a.Op() == ir.OSLICELIT {
189214
// variadic args are encoded as slice literal.
190215
s := a.(*ir.CompLitExpr)
191-
ns := []*ir.Name{}
192-
for i, n := range s.List {
193-
if name := getNameFromNode(n); name != nil {
194-
debugName(name, ir.Line(a))
216+
ns := []ir.Node{}
217+
for i, elem := range s.List {
218+
if name := getNameFromNode(elem); name != nil {
219+
debugName(name, n.Pos())
195220
ns = append(ns, name)
196221
} else {
197222
// We need a temporary to save this arg.
198-
tmp := typecheck.TempAt(n.Pos(), curFn, n.Type())
199-
argTmps = append(argTmps, typecheck.AssignExpr(ir.NewAssignStmt(n.Pos(), tmp, n)))
223+
tmp := typecheck.TempAt(elem.Pos(), curFn, elem.Type())
224+
argTmps = append(argTmps, typecheck.AssignExpr(ir.NewAssignStmt(elem.Pos(), tmp, elem)))
200225
names = append(names, tmp)
201226
s.List[i] = tmp
202-
if base.Flag.LowerM > 0 {
203-
fmt.Printf("%v: function arg will be kept alive\n", ir.Line(n))
227+
if base.Flag.LowerM > 1 {
228+
base.WarnfAt(n.Pos(), "function arg will be kept alive")
204229
}
205230
}
206231
}
@@ -212,17 +237,17 @@ func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
212237
argTmps = append(argTmps, typecheck.AssignExpr(ir.NewAssignStmt(n.Pos(), tmp, a)))
213238
names = append(names, tmp)
214239
n.Args[i] = tmp
215-
if base.Flag.LowerM > 0 {
216-
fmt.Printf("%v: function arg will be kept alive\n", ir.Line(stmt))
240+
if base.Flag.LowerM > 1 {
241+
base.WarnfAt(n.Pos(), "function arg will be kept alive")
217242
}
218243
}
219244
}
220245
if len(argTmps) > 0 {
221246
argTmps = append(argTmps, n)
222247
curNode = ir.NewBlockStmt(n.Pos(), argTmps)
223248
}
249+
ret = keepAliveAt(names, curNode)
224250
}
225-
ret = keepAliveAt(names, curNode)
226251
}
227252
return
228253
}
@@ -282,6 +307,8 @@ func (e editor) edit(n ir.Node) ir.Node {
282307
preserveStmts(e.curFn, n.Body)
283308
case *ir.CommClause:
284309
preserveStmts(e.curFn, n.Body)
310+
case *ir.RangeStmt:
311+
preserveStmts(e.curFn, n.Body)
285312
}
286313
}
287314
return n

‎test/bloop.go‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// errorcheck -0 -m
1+
// errorcheck -0 -m=2
22

33
// Copyright 2025 The Go Authors. All rights reserved.
44
// Use of this source code is governed by a BSD-style
@@ -25,10 +25,11 @@ func caninlineVariadic(x ...int) { // ERROR "can inline caninlineVariadic" "x do
2525
something = x[0]
2626
}
2727

28-
func test(b *testing.B, localsink, cond int) { // ERROR "leaking param: b"
28+
func test(b *testing.B, localsink, cond int) { // ERROR ".*"
2929
for i := 0; i < b.N; i++ {
3030
caninline(1) // ERROR "inlining call to caninline"
3131
}
32+
somethingptr := &something
3233
for b.Loop() { // ERROR "inlining call to testing\.\(\*B\)\.Loop"
3334
caninline(1) // ERROR "inlining call to caninline" "function result will be kept alive" ".* does not escape"
3435
caninlineNoRet(1) // ERROR "inlining call to caninlineNoRet" "function arg will be kept alive" ".* does not escape"
@@ -37,6 +38,7 @@ func test(b *testing.B, localsink, cond int) { // ERROR "leaking param: b"
3738
localsink = caninline(1) // ERROR "inlining call to caninline" "localsink will be kept alive" ".* does not escape"
3839
localsink += 5 // ERROR "localsink will be kept alive" ".* does not escape"
3940
localsink, cond = 1, 2 // ERROR "localsink will be kept alive" "cond will be kept alive" ".* does not escape"
41+
*somethingptr = 1 // ERROR "dereference will be kept alive"
4042
if cond > 0 {
4143
caninline(1) // ERROR "inlining call to caninline" "function result will be kept alive" ".* does not escape"
4244
}

0 commit comments

Comments
 (0)