@@ -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.
20622080const 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