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.heapLoc)
40
41 var walkgen uint32
42 for len(todo) > 0 {
43 root := todo[len(todo)-1]
44 todo = todo[:len(todo)-1]
45 root.queued = false
46
47 walkgen++
48 b.walkOne(root, walkgen, enqueue)
49 }
50 }
51
52
53
54 func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) {
55
56
57
58
59
60 root.walkgen = walkgen
61 root.derefs = 0
62 root.dst = nil
63
64 todo := []*location{root}
65 for len(todo) > 0 {
66 l := todo[len(todo)-1]
67 todo = todo[:len(todo)-1]
68
69 derefs := l.derefs
70
71
72 addressOf := derefs < 0
73 if addressOf {
74
75
76
77
78 derefs = 0
79
80
81
82
83 if !root.transient && l.transient {
84 l.transient = false
85 enqueue(l)
86 }
87 }
88
89 if b.outlives(root, l) {
90
91
92
93
94
95 if l.isName(ir.PPARAM) {
96 if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.escapes {
97 if base.Flag.LowerM >= 2 {
98 fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs)
99 }
100 explanation := b.explainPath(root, l)
101 if logopt.Enabled() {
102 var e_curfn *ir.Func
103 logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e_curfn),
104 fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, b.explainLoc(root), derefs), explanation)
105 }
106 }
107 l.leakTo(root, derefs)
108 }
109
110
111
112
113 if addressOf && !l.escapes {
114 if logopt.Enabled() || base.Flag.LowerM >= 2 {
115 if base.Flag.LowerM >= 2 {
116 fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n)
117 }
118 explanation := b.explainPath(root, l)
119 if logopt.Enabled() {
120 var e_curfn *ir.Func
121 logopt.LogOpt(l.n.Pos(), "escape", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", l.n), explanation)
122 }
123 }
124 l.escapes = true
125 enqueue(l)
126 continue
127 }
128 }
129
130 for i, edge := range l.edges {
131 if edge.src.escapes {
132 continue
133 }
134 d := derefs + edge.derefs
135 if edge.src.walkgen != walkgen || edge.src.derefs > d {
136 edge.src.walkgen = walkgen
137 edge.src.derefs = d
138 edge.src.dst = l
139 edge.src.dstEdgeIdx = i
140 todo = append(todo, edge.src)
141 }
142 }
143 }
144 }
145
146
147 func (b *batch) explainPath(root, src *location) []*logopt.LoggedOpt {
148 visited := make(map[*location]bool)
149 pos := base.FmtPos(src.n.Pos())
150 var explanation []*logopt.LoggedOpt
151 for {
152
153 if visited[src] {
154 if base.Flag.LowerM >= 2 {
155 fmt.Printf("%s: warning: truncated explanation due to assignment cycle; see golang.org/issue/35518\n", pos)
156 }
157 break
158 }
159 visited[src] = true
160 dst := src.dst
161 edge := &dst.edges[src.dstEdgeIdx]
162 if edge.src != src {
163 base.Fatalf("path inconsistency: %v != %v", edge.src, src)
164 }
165
166 explanation = b.explainFlow(pos, dst, src, edge.derefs, edge.notes, explanation)
167
168 if dst == root {
169 break
170 }
171 src = dst
172 }
173
174 return explanation
175 }
176
177 func (b *batch) explainFlow(pos string, dst, srcloc *location, derefs int, notes *note, explanation []*logopt.LoggedOpt) []*logopt.LoggedOpt {
178 ops := "&"
179 if derefs >= 0 {
180 ops = strings.Repeat("*", derefs)
181 }
182 print := base.Flag.LowerM >= 2
183
184 flow := fmt.Sprintf(" flow: %s = %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc))
185 if print {
186 fmt.Printf("%s:%s\n", pos, flow)
187 }
188 if logopt.Enabled() {
189 var epos src.XPos
190 if notes != nil {
191 epos = notes.where.Pos()
192 } else if srcloc != nil && srcloc.n != nil {
193 epos = srcloc.n.Pos()
194 }
195 var e_curfn *ir.Func
196 explanation = append(explanation, logopt.NewLoggedOpt(epos, "escflow", "escape", ir.FuncName(e_curfn), flow))
197 }
198
199 for note := notes; note != nil; note = note.next {
200 if print {
201 fmt.Printf("%s: from %v (%v) at %s\n", pos, note.where, note.why, base.FmtPos(note.where.Pos()))
202 }
203 if logopt.Enabled() {
204 var e_curfn *ir.Func
205 explanation = append(explanation, logopt.NewLoggedOpt(note.where.Pos(), "escflow", "escape", ir.FuncName(e_curfn),
206 fmt.Sprintf(" from %v (%v)", note.where, note.why)))
207 }
208 }
209 return explanation
210 }
211
212 func (b *batch) explainLoc(l *location) string {
213 if l == &b.heapLoc {
214 return "{heap}"
215 }
216 if l.n == nil {
217
218 return "{temp}"
219 }
220 if l.n.Op() == ir.ONAME {
221 return fmt.Sprintf("%v", l.n)
222 }
223 return fmt.Sprintf("{storage for %v}", l.n)
224 }
225
226
227
228 func (b *batch) outlives(l, other *location) bool {
229
230 if l.escapes {
231 return true
232 }
233
234
235
236
237 if l.isName(ir.PPARAMOUT) {
238
239
240
241
242
243
244 if containsClosure(other.curfn, l.curfn) && l.curfn.ClosureCalled() {
245 return false
246 }
247
248 return true
249 }
250
251
252
253
254
255
256
257
258
259 if l.curfn == other.curfn && l.loopDepth < other.loopDepth {
260 return true
261 }
262
263
264
265
266
267
268
269
270 if containsClosure(l.curfn, other.curfn) {
271 return true
272 }
273
274 return false
275 }
276
277
278 func containsClosure(f, c *ir.Func) bool {
279
280 if f == c {
281 return false
282 }
283
284
285
286 fn := f.Sym().Name
287 cn := c.Sym().Name
288 return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.'
289 }
290
View as plain text