1
2
3
4
5 package escape
6
7 import (
8 "cmd/compile/internal/base"
9 "cmd/compile/internal/ir"
10 "cmd/compile/internal/logopt"
11 "cmd/internal/src"
12 "fmt"
13 "strings"
14 )
15
16
17
18 func (b *batch) walkAll() {
19
20
21
22
23
24
25
26
27
28 todo := make([]*location, 0, len(b.allLocs)+1)
29 enqueue := func(loc *location) {
30 if !loc.queued {
31 todo = append(todo, loc)
32 loc.queued = true
33 }
34 }
35
36 for _, loc := range b.allLocs {
37 enqueue(loc)
38 }
39 enqueue(&b.mutatorLoc)
40 enqueue(&b.calleeLoc)
41 enqueue(&b.heapLoc)
42
43 var walkgen uint32
44 for len(todo) > 0 {
45 root := todo[len(todo)-1]
46 todo = todo[:len(todo)-1]
47 root.queued = false
48
49 walkgen++
50 b.walkOne(root, walkgen, enqueue)
51 }
52 }
53
54
55
56 func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) {
57
58
59
60
61
62 root.walkgen = walkgen
63 root.derefs = 0
64 root.dst = nil
65
66 if root.hasAttr(attrCalls) {
67 if clo, ok := root.n.(*ir.ClosureExpr); ok {
68 if fn := clo.Func; b.inMutualBatch(fn.Nname) && !fn.ClosureResultsLost() {
69 fn.SetClosureResultsLost(true)
70
71
72
73 for _, result := range fn.Type().Results() {
74 enqueue(b.oldLoc(result.Nname.(*ir.Name)))
75 }
76 }
77 }
78 }
79
80 todo := []*location{root}
81 for len(todo) > 0 {
82 l := todo[len(todo)-1]
83 todo = todo[:len(todo)-1]
84
85 derefs := l.derefs
86 var newAttrs locAttr
87
88
89 addressOf := derefs < 0
90 if addressOf {
91
92
93
94
95 derefs = 0
96
97
98
99
100 if b.outlives(root, l) {
101 if !l.hasAttr(attrEscapes) && (logopt.Enabled() || base.Flag.LowerM >= 2) {
102 if base.Flag.LowerM >= 2 {
103 fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n)
104 }
105 explanation := b.explainPath(root, l)
106 if logopt.Enabled() {
107 var e_curfn *ir.Func
108 logopt.LogOpt(l.n.Pos(), "escape", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", l.n), explanation)
109 }
110 }
111 newAttrs |= attrEscapes | attrPersists | attrMutates | attrCalls
112 } else
113
114
115 if root.hasAttr(attrPersists) {
116 newAttrs |= attrPersists
117 }
118 }
119
120 if derefs == 0 {
121 newAttrs |= root.attrs & (attrMutates | attrCalls)
122 }
123
124
125
126
127
128
129 if l.isName(ir.PPARAM) {
130 if b.outlives(root, l) {
131 if !l.hasAttr(attrEscapes) && (logopt.Enabled() || base.Flag.LowerM >= 2) {
132 if base.Flag.LowerM >= 2 {
133 fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs)
134 }
135 explanation := b.explainPath(root, l)
136 if logopt.Enabled() {
137 var e_curfn *ir.Func
138 logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e_curfn),
139 fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, b.explainLoc(root), derefs), explanation)
140 }
141 }
142 l.leakTo(root, derefs)
143 }
144 if root.hasAttr(attrMutates) {
145 l.paramEsc.AddMutator(derefs)
146 }
147 if root.hasAttr(attrCalls) {
148 l.paramEsc.AddCallee(derefs)
149 }
150 }
151
152 if newAttrs&^l.attrs != 0 {
153 l.attrs |= newAttrs
154 enqueue(l)
155 if l.attrs&attrEscapes != 0 {
156 continue
157 }
158 }
159
160 for i, edge := range l.edges {
161 if edge.src.hasAttr(attrEscapes) {
162 continue
163 }
164 d := derefs + edge.derefs
165 if edge.src.walkgen != walkgen || edge.src.derefs > d {
166 edge.src.walkgen = walkgen
167 edge.src.derefs = d
168 edge.src.dst = l
169 edge.src.dstEdgeIdx = i
170 todo = append(todo, edge.src)
171 }
172 }
173 }
174 }
175
176
177 func (b *batch) explainPath(root, src *location) []*logopt.LoggedOpt {
178 visited := make(map[*location]bool)
179 pos := base.FmtPos(src.n.Pos())
180 var explanation []*logopt.LoggedOpt
181 for {
182
183 if visited[src] {
184 if base.Flag.LowerM >= 2 {
185 fmt.Printf("%s: warning: truncated explanation due to assignment cycle; see golang.org/issue/35518\n", pos)
186 }
187 break
188 }
189 visited[src] = true
190 dst := src.dst
191 edge := &dst.edges[src.dstEdgeIdx]
192 if edge.src != src {
193 base.Fatalf("path inconsistency: %v != %v", edge.src, src)
194 }
195
196 explanation = b.explainFlow(pos, dst, src, edge.derefs, edge.notes, explanation)
197
198 if dst == root {
199 break
200 }
201 src = dst
202 }
203
204 return explanation
205 }
206
207 func (b *batch) explainFlow(pos string, dst, srcloc *location, derefs int, notes *note, explanation []*logopt.LoggedOpt) []*logopt.LoggedOpt {
208 ops := "&"
209 if derefs >= 0 {
210 ops = strings.Repeat("*", derefs)
211 }
212 print := base.Flag.LowerM >= 2
213
214 flow := fmt.Sprintf(" flow: %s = %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc))
215 if print {
216 fmt.Printf("%s:%s\n", pos, flow)
217 }
218 if logopt.Enabled() {
219 var epos src.XPos
220 if notes != nil {
221 epos = notes.where.Pos()
222 } else if srcloc != nil && srcloc.n != nil {
223 epos = srcloc.n.Pos()
224 }
225 var e_curfn *ir.Func
226 explanation = append(explanation, logopt.NewLoggedOpt(epos, epos, "escflow", "escape", ir.FuncName(e_curfn), flow))
227 }
228
229 for note := notes; note != nil; note = note.next {
230 if print {
231 fmt.Printf("%s: from %v (%v) at %s\n", pos, note.where, note.why, base.FmtPos(note.where.Pos()))
232 }
233 if logopt.Enabled() {
234 var e_curfn *ir.Func
235 notePos := note.where.Pos()
236 explanation = append(explanation, logopt.NewLoggedOpt(notePos, notePos, "escflow", "escape", ir.FuncName(e_curfn),
237 fmt.Sprintf(" from %v (%v)", note.where, note.why)))
238 }
239 }
240 return explanation
241 }
242
243 func (b *batch) explainLoc(l *location) string {
244 if l == &b.heapLoc {
245 return "{heap}"
246 }
247 if l.n == nil {
248
249 return "{temp}"
250 }
251 if l.n.Op() == ir.ONAME {
252 return fmt.Sprintf("%v", l.n)
253 }
254 return fmt.Sprintf("{storage for %v}", l.n)
255 }
256
257
258
259 func (b *batch) outlives(l, other *location) bool {
260
261 if l.hasAttr(attrEscapes) {
262 return true
263 }
264
265
266 if l == &b.mutatorLoc || l == &b.calleeLoc {
267 return false
268 }
269
270
271
272
273 if l.isName(ir.PPARAMOUT) {
274
275
276
277
278
279
280
281 if containsClosure(other.curfn, l.curfn) && !l.curfn.ClosureResultsLost() {
282 return false
283 }
284
285 return true
286 }
287
288
289
290
291
292
293
294
295
296 if l.curfn == other.curfn && l.loopDepth < other.loopDepth {
297 return true
298 }
299
300
301
302
303
304
305
306
307 if containsClosure(l.curfn, other.curfn) {
308 return true
309 }
310
311 return false
312 }
313
314
315 func containsClosure(f, c *ir.Func) bool {
316
317 if f == c || c.OClosure == nil {
318 return false
319 }
320
321 for p := c.ClosureParent; p != nil; p = p.ClosureParent {
322 if p == f {
323 return true
324 }
325 }
326 return false
327 }
328
View as plain text