Skip to content

Commit 67ad695

Browse files
committed
[dev.regabi] cmd/compile: split escape analysis state
In a future CL, I plan to change escape analysis to walk function literal bodies at the point they appear within the AST, rather than separately as their own standalone function declaration. This means escape analysis's AST-walking code will become reentrant. To make this easier to get right, this CL splits escape analysis's state into two separate types: one that holds all of the state shared across the entire batch, and another that holds only the state that's used within initFunc and walkFunc. Incidentally, this CL reveals that a bunch of logopt code was using e.curfn outside of the AST-walking code paths where it's actually set, so it was always nil. That code is in need of refactoring anyway, so I'll come back and figure out the correct values to pass later when I address that. Passes toolstash -cmp. Change-Id: I1d13f47d06f7583401afa1b53fcc5ee2adaea6c8 Reviewed-on: https://go-review.googlesource.com/c/go/+/280997 Trust: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
1 parent fad9a8b commit 67ad695

File tree

1 file changed

+70
-52
lines changed

1 file changed

+70
-52
lines changed

src/cmd/compile/internal/escape/escape.go

Lines changed: 70 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -85,20 +85,29 @@ import (
8585
// u[2], etc. However, we do record the implicit dereference involved
8686
// in indexing a slice.
8787

88-
type escape struct {
88+
// A batch holds escape analysis state that's shared across an entire
89+
// batch of functions being analyzed at once.
90+
type batch struct {
8991
allLocs []*location
90-
labels map[*types.Sym]labelState // known labels
9192

92-
curfn *ir.Func
93+
heapLoc location
94+
blankLoc location
95+
}
96+
97+
// An escape holds state specific to a single function being analyzed
98+
// within a batch.
99+
type escape struct {
100+
*batch
101+
102+
curfn *ir.Func // function being analyzed
103+
104+
labels map[*types.Sym]labelState // known labels
93105

94106
// loopDepth counts the current loop nesting depth within
95107
// curfn. It increments within each "for" loop and at each
96108
// label with a corresponding backwards "goto" (i.e.,
97109
// unstructured loop).
98110
loopDepth int
99-
100-
heapLoc location
101-
blankLoc location
102111
}
103112

104113
// An location represents an abstract location that stores a Go
@@ -167,11 +176,11 @@ func Fmt(n ir.Node) string {
167176

168177
if n.Op() == ir.ONAME {
169178
n := n.(*ir.Name)
170-
if e, ok := n.Opt.(*location); ok && e.loopDepth != 0 {
179+
if loc, ok := n.Opt.(*location); ok && loc.loopDepth != 0 {
171180
if text != "" {
172181
text += " "
173182
}
174-
text += fmt.Sprintf("ld(%d)", e.loopDepth)
183+
text += fmt.Sprintf("ld(%d)", loc.loopDepth)
175184
}
176185
}
177186

@@ -187,23 +196,31 @@ func Batch(fns []*ir.Func, recursive bool) {
187196
}
188197
}
189198

190-
var e escape
191-
e.heapLoc.escapes = true
199+
var b batch
200+
b.heapLoc.escapes = true
192201

193202
// Construct data-flow graph from syntax trees.
194203
for _, fn := range fns {
195-
e.initFunc(fn)
204+
b.with(fn).initFunc()
196205
}
197206
for _, fn := range fns {
198-
e.walkFunc(fn)
207+
b.with(fn).walkFunc()
199208
}
200-
e.curfn = nil
201209

202-
e.walkAll()
203-
e.finish(fns)
210+
b.walkAll()
211+
b.finish(fns)
212+
}
213+
214+
func (b *batch) with(fn *ir.Func) *escape {
215+
return &escape{
216+
batch: b,
217+
curfn: fn,
218+
loopDepth: 1,
219+
}
204220
}
205221

206-
func (e *escape) initFunc(fn *ir.Func) {
222+
func (e *escape) initFunc() {
223+
fn := e.curfn
207224
if fn.Esc() != escFuncUnknown {
208225
base.Fatalf("unexpected node: %v", fn)
209226
}
@@ -212,9 +229,6 @@ func (e *escape) initFunc(fn *ir.Func) {
212229
ir.Dump("escAnalyze", fn)
213230
}
214231

215-
e.curfn = fn
216-
e.loopDepth = 1
217-
218232
// Allocate locations for local variables.
219233
for _, dcl := range fn.Dcl {
220234
if dcl.Op() == ir.ONAME {
@@ -223,7 +237,8 @@ func (e *escape) initFunc(fn *ir.Func) {
223237
}
224238
}
225239

226-
func (e *escape) walkFunc(fn *ir.Func) {
240+
func (e *escape) walkFunc() {
241+
fn := e.curfn
227242
fn.SetEsc(escFuncStarted)
228243

229244
// Identify labels that mark the head of an unstructured loop.
@@ -246,8 +261,6 @@ func (e *escape) walkFunc(fn *ir.Func) {
246261
}
247262
})
248263

249-
e.curfn = fn
250-
e.loopDepth = 1
251264
e.block(fn.Body)
252265

253266
if len(e.labels) != 0 {
@@ -680,9 +693,9 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {
680693

681694
case ir.OCLOSURE:
682695
n := n.(*ir.ClosureExpr)
683-
k = e.spill(k, n)
684696

685697
// Link addresses of captured variables to closure.
698+
k = e.spill(k, n)
686699
for _, v := range n.Func.ClosureVars {
687700
k := k
688701
if !v.Byval() {
@@ -1174,7 +1187,7 @@ func (e *escape) newLoc(n ir.Node, transient bool) *location {
11741187
return loc
11751188
}
11761189

1177-
func (e *escape) oldLoc(n *ir.Name) *location {
1190+
func (b *batch) oldLoc(n *ir.Name) *location {
11781191
n = canonicalNode(n).(*ir.Name)
11791192
return n.Opt.(*location)
11801193
}
@@ -1216,7 +1229,7 @@ func (e *escape) discardHole() hole { return e.blankLoc.asHole() }
12161229

12171230
// walkAll computes the minimal dereferences between all pairs of
12181231
// locations.
1219-
func (e *escape) walkAll() {
1232+
func (b *batch) walkAll() {
12201233
// We use a work queue to keep track of locations that we need
12211234
// to visit, and repeatedly walk until we reach a fixed point.
12221235
//
@@ -1226,18 +1239,18 @@ func (e *escape) walkAll() {
12261239
// happen at most once. So we take Θ(len(e.allLocs)) walks.
12271240

12281241
// LIFO queue, has enough room for e.allLocs and e.heapLoc.
1229-
todo := make([]*location, 0, len(e.allLocs)+1)
1242+
todo := make([]*location, 0, len(b.allLocs)+1)
12301243
enqueue := func(loc *location) {
12311244
if !loc.queued {
12321245
todo = append(todo, loc)
12331246
loc.queued = true
12341247
}
12351248
}
12361249

1237-
for _, loc := range e.allLocs {
1250+
for _, loc := range b.allLocs {
12381251
enqueue(loc)
12391252
}
1240-
enqueue(&e.heapLoc)
1253+
enqueue(&b.heapLoc)
12411254

12421255
var walkgen uint32
12431256
for len(todo) > 0 {
@@ -1246,13 +1259,13 @@ func (e *escape) walkAll() {
12461259
root.queued = false
12471260

12481261
walkgen++
1249-
e.walkOne(root, walkgen, enqueue)
1262+
b.walkOne(root, walkgen, enqueue)
12501263
}
12511264
}
12521265

12531266
// walkOne computes the minimal number of dereferences from root to
12541267
// all other locations.
1255-
func (e *escape) walkOne(root *location, walkgen uint32, enqueue func(*location)) {
1268+
func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) {
12561269
// The data flow graph has negative edges (from addressing
12571270
// operations), so we use the Bellman-Ford algorithm. However,
12581271
// we don't have to worry about infinite negative cycles since
@@ -1287,7 +1300,7 @@ func (e *escape) walkOne(root *location, walkgen uint32, enqueue func(*location)
12871300
}
12881301
}
12891302

1290-
if e.outlives(root, l) {
1303+
if b.outlives(root, l) {
12911304
// l's value flows to root. If l is a function
12921305
// parameter and root is the heap or a
12931306
// corresponding result parameter, then record
@@ -1296,12 +1309,13 @@ func (e *escape) walkOne(root *location, walkgen uint32, enqueue func(*location)
12961309
if l.isName(ir.PPARAM) {
12971310
if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.escapes {
12981311
if base.Flag.LowerM >= 2 {
1299-
fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, e.explainLoc(root), derefs)
1312+
fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs)
13001313
}
1301-
explanation := e.explainPath(root, l)
1314+
explanation := b.explainPath(root, l)
13021315
if logopt.Enabled() {
1303-
logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e.curfn),
1304-
fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, e.explainLoc(root), derefs), explanation)
1316+
var e_curfn *ir.Func // TODO(mdempsky): Fix.
1317+
logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e_curfn),
1318+
fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, b.explainLoc(root), derefs), explanation)
13051319
}
13061320
}
13071321
l.leakTo(root, derefs)
@@ -1315,9 +1329,10 @@ func (e *escape) walkOne(root *location, walkgen uint32, enqueue func(*location)
13151329
if base.Flag.LowerM >= 2 {
13161330
fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n)
13171331
}
1318-
explanation := e.explainPath(root, l)
1332+
explanation := b.explainPath(root, l)
13191333
if logopt.Enabled() {
1320-
logopt.LogOpt(l.n.Pos(), "escape", "escape", ir.FuncName(e.curfn), fmt.Sprintf("%v escapes to heap", l.n), explanation)
1334+
var e_curfn *ir.Func // TODO(mdempsky): Fix.
1335+
logopt.LogOpt(l.n.Pos(), "escape", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", l.n), explanation)
13211336
}
13221337
}
13231338
l.escapes = true
@@ -1343,7 +1358,7 @@ func (e *escape) walkOne(root *location, walkgen uint32, enqueue func(*location)
13431358
}
13441359

13451360
// explainPath prints an explanation of how src flows to the walk root.
1346-
func (e *escape) explainPath(root, src *location) []*logopt.LoggedOpt {
1361+
func (b *batch) explainPath(root, src *location) []*logopt.LoggedOpt {
13471362
visited := make(map[*location]bool)
13481363
pos := base.FmtPos(src.n.Pos())
13491364
var explanation []*logopt.LoggedOpt
@@ -1362,7 +1377,7 @@ func (e *escape) explainPath(root, src *location) []*logopt.LoggedOpt {
13621377
base.Fatalf("path inconsistency: %v != %v", edge.src, src)
13631378
}
13641379

1365-
explanation = e.explainFlow(pos, dst, src, edge.derefs, edge.notes, explanation)
1380+
explanation = b.explainFlow(pos, dst, src, edge.derefs, edge.notes, explanation)
13661381

13671382
if dst == root {
13681383
break
@@ -1373,14 +1388,14 @@ func (e *escape) explainPath(root, src *location) []*logopt.LoggedOpt {
13731388
return explanation
13741389
}
13751390

1376-
func (e *escape) explainFlow(pos string, dst, srcloc *location, derefs int, notes *note, explanation []*logopt.LoggedOpt) []*logopt.LoggedOpt {
1391+
func (b *batch) explainFlow(pos string, dst, srcloc *location, derefs int, notes *note, explanation []*logopt.LoggedOpt) []*logopt.LoggedOpt {
13771392
ops := "&"
13781393
if derefs >= 0 {
13791394
ops = strings.Repeat("*", derefs)
13801395
}
13811396
print := base.Flag.LowerM >= 2
13821397

1383-
flow := fmt.Sprintf(" flow: %s = %s%v:", e.explainLoc(dst), ops, e.explainLoc(srcloc))
1398+
flow := fmt.Sprintf(" flow: %s = %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc))
13841399
if print {
13851400
fmt.Printf("%s:%s\n", pos, flow)
13861401
}
@@ -1391,23 +1406,25 @@ func (e *escape) explainFlow(pos string, dst, srcloc *location, derefs int, note
13911406
} else if srcloc != nil && srcloc.n != nil {
13921407
epos = srcloc.n.Pos()
13931408
}
1394-
explanation = append(explanation, logopt.NewLoggedOpt(epos, "escflow", "escape", ir.FuncName(e.curfn), flow))
1409+
var e_curfn *ir.Func // TODO(mdempsky): Fix.
1410+
explanation = append(explanation, logopt.NewLoggedOpt(epos, "escflow", "escape", ir.FuncName(e_curfn), flow))
13951411
}
13961412

13971413
for note := notes; note != nil; note = note.next {
13981414
if print {
13991415
fmt.Printf("%s: from %v (%v) at %s\n", pos, note.where, note.why, base.FmtPos(note.where.Pos()))
14001416
}
14011417
if logopt.Enabled() {
1402-
explanation = append(explanation, logopt.NewLoggedOpt(note.where.Pos(), "escflow", "escape", ir.FuncName(e.curfn),
1418+
var e_curfn *ir.Func // TODO(mdempsky): Fix.
1419+
explanation = append(explanation, logopt.NewLoggedOpt(note.where.Pos(), "escflow", "escape", ir.FuncName(e_curfn),
14031420
fmt.Sprintf(" from %v (%v)", note.where, note.why)))
14041421
}
14051422
}
14061423
return explanation
14071424
}
14081425

1409-
func (e *escape) explainLoc(l *location) string {
1410-
if l == &e.heapLoc {
1426+
func (b *batch) explainLoc(l *location) string {
1427+
if l == &b.heapLoc {
14111428
return "{heap}"
14121429
}
14131430
if l.n == nil {
@@ -1422,7 +1439,7 @@ func (e *escape) explainLoc(l *location) string {
14221439

14231440
// outlives reports whether values stored in l may survive beyond
14241441
// other's lifetime if stack allocated.
1425-
func (e *escape) outlives(l, other *location) bool {
1442+
func (b *batch) outlives(l, other *location) bool {
14261443
// The heap outlives everything.
14271444
if l.escapes {
14281445
return true
@@ -1503,7 +1520,7 @@ func (l *location) leakTo(sink *location, derefs int) {
15031520
l.paramEsc.AddHeap(derefs)
15041521
}
15051522

1506-
func (e *escape) finish(fns []*ir.Func) {
1523+
func (b *batch) finish(fns []*ir.Func) {
15071524
// Record parameter tags for package export data.
15081525
for _, fn := range fns {
15091526
fn.SetEsc(escFuncTagged)
@@ -1512,12 +1529,12 @@ func (e *escape) finish(fns []*ir.Func) {
15121529
for _, fs := range &types.RecvsParams {
15131530
for _, f := range fs(fn.Type()).Fields().Slice() {
15141531
narg++
1515-
f.Note = e.paramTag(fn, narg, f)
1532+
f.Note = b.paramTag(fn, narg, f)
15161533
}
15171534
}
15181535
}
15191536

1520-
for _, loc := range e.allLocs {
1537+
for _, loc := range b.allLocs {
15211538
n := loc.n
15221539
if n == nil {
15231540
continue
@@ -1535,7 +1552,8 @@ func (e *escape) finish(fns []*ir.Func) {
15351552
base.WarnfAt(n.Pos(), "%v escapes to heap", n)
15361553
}
15371554
if logopt.Enabled() {
1538-
logopt.LogOpt(n.Pos(), "escape", "escape", ir.FuncName(e.curfn))
1555+
var e_curfn *ir.Func // TODO(mdempsky): Fix.
1556+
logopt.LogOpt(n.Pos(), "escape", "escape", ir.FuncName(e_curfn))
15391557
}
15401558
}
15411559
n.SetEsc(ir.EscHeap)
@@ -2061,7 +2079,7 @@ const UnsafeUintptrNote = "unsafe-uintptr"
20612079
// marked go:uintptrescapes.
20622080
const UintptrEscapesNote = "uintptr-escapes"
20632081

2064-
func (e *escape) paramTag(fn *ir.Func, narg int, f *types.Field) string {
2082+
func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string {
20652083
name := func() string {
20662084
if f.Sym != nil {
20672085
return f.Sym.Name
@@ -2132,7 +2150,7 @@ func (e *escape) paramTag(fn *ir.Func, narg int, f *types.Field) string {
21322150
}
21332151

21342152
n := f.Nname.(*ir.Name)
2135-
loc := e.oldLoc(n)
2153+
loc := b.oldLoc(n)
21362154
esc := loc.paramEsc
21372155
esc.Optimize()
21382156

0 commit comments

Comments
 (0)