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/compile/internal/types"
12 "fmt"
13 )
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 type location struct {
44 n ir.Node
45 curfn *ir.Func
46 edges []edge
47 loopDepth int
48
49
50
51
52 resultIndex int
53
54
55
56 derefs int
57 walkgen uint32
58
59
60
61
62 dst *location
63 dstEdgeIdx int
64
65
66
67 queuedWalkAll bool
68
69
70
71
72 queuedWalkOne uint32
73
74
75 attrs locAttr
76
77
78 paramEsc leaks
79
80 captured bool
81 reassigned bool
82 addrtaken bool
83 param bool
84 paramOut bool
85 }
86
87 type locAttr uint8
88
89 const (
90
91
92 attrEscapes locAttr = 1 << iota
93
94
95
96
97 attrPersists
98
99
100
101
102
103 attrMutates
104
105
106
107
108 attrCalls
109 )
110
111 func (l *location) hasAttr(attr locAttr) bool { return l.attrs&attr != 0 }
112
113
114 type edge struct {
115 src *location
116 derefs int
117 notes *note
118 }
119
120 func (l *location) asHole() hole {
121 return hole{dst: l}
122 }
123
124
125 func (l *location) leakTo(sink *location, derefs int) {
126
127
128
129 if !sink.hasAttr(attrEscapes) && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn {
130 ri := sink.resultIndex - 1
131 if ri < numEscResults {
132
133 l.paramEsc.AddResult(ri, derefs)
134 return
135 }
136 }
137
138
139 l.paramEsc.AddHeap(derefs)
140 }
141
142 func (l *location) isName(c ir.Class) bool {
143 return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class == c
144 }
145
146
147
148
149 type hole struct {
150 dst *location
151 derefs int
152 notes *note
153
154
155
156
157 addrtaken bool
158 }
159
160 type note struct {
161 next *note
162 where ir.Node
163 why string
164 }
165
166 func (k hole) note(where ir.Node, why string) hole {
167 if where == nil || why == "" {
168 base.Fatalf("note: missing where/why")
169 }
170 if base.Flag.LowerM >= 2 || logopt.Enabled() {
171 k.notes = ¬e{
172 next: k.notes,
173 where: where,
174 why: why,
175 }
176 }
177 return k
178 }
179
180 func (k hole) shift(delta int) hole {
181 k.derefs += delta
182 if k.derefs < -1 {
183 base.Fatalf("derefs underflow: %v", k.derefs)
184 }
185 k.addrtaken = delta < 0
186 return k
187 }
188
189 func (k hole) deref(where ir.Node, why string) hole { return k.shift(1).note(where, why) }
190 func (k hole) addr(where ir.Node, why string) hole { return k.shift(-1).note(where, why) }
191
192 func (k hole) dotType(t *types.Type, where ir.Node, why string) hole {
193 if !t.IsInterface() && !types.IsDirectIface(t) {
194 k = k.shift(1)
195 }
196 return k.note(where, why)
197 }
198
199 func (b *batch) flow(k hole, src *location) {
200 if k.addrtaken {
201 src.addrtaken = true
202 }
203
204 dst := k.dst
205 if dst == &b.blankLoc {
206 return
207 }
208 if dst == src && k.derefs >= 0 {
209 return
210 }
211 if dst.hasAttr(attrEscapes) && k.derefs < 0 {
212 if base.Flag.LowerM >= 2 || logopt.Enabled() {
213 pos := base.FmtPos(src.n.Pos())
214 if base.Flag.LowerM >= 2 {
215 fmt.Printf("%s: %v escapes to heap in %v:\n", pos, src.n, ir.FuncName(src.curfn))
216 }
217 explanation := b.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{})
218 if logopt.Enabled() {
219 var e_curfn *ir.Func
220 logopt.LogOpt(src.n.Pos(), "escapes", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", src.n), explanation)
221 }
222
223 }
224 src.attrs |= attrEscapes | attrPersists | attrMutates | attrCalls
225 return
226 }
227
228
229 dst.edges = append(dst.edges, edge{src: src, derefs: k.derefs, notes: k.notes})
230 }
231
232 func (b *batch) heapHole() hole { return b.heapLoc.asHole() }
233 func (b *batch) mutatorHole() hole { return b.mutatorLoc.asHole() }
234 func (b *batch) calleeHole() hole { return b.calleeLoc.asHole() }
235 func (b *batch) discardHole() hole { return b.blankLoc.asHole() }
236
237 func (b *batch) oldLoc(n *ir.Name) *location {
238 if n.Canonical().Opt == nil {
239 base.FatalfAt(n.Pos(), "%v has no location", n)
240 }
241 return n.Canonical().Opt.(*location)
242 }
243
244 func (e *escape) newLoc(n ir.Node, persists bool) *location {
245 if e.curfn == nil {
246 base.Fatalf("e.curfn isn't set")
247 }
248 if n != nil && n.Type() != nil && n.Type().NotInHeap() {
249 base.ErrorfAt(n.Pos(), 0, "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type())
250 }
251
252 if n != nil && n.Op() == ir.ONAME {
253 if canon := n.(*ir.Name).Canonical(); n != canon {
254 base.FatalfAt(n.Pos(), "newLoc on non-canonical %v (canonical is %v)", n, canon)
255 }
256 }
257 loc := &location{
258 n: n,
259 curfn: e.curfn,
260 loopDepth: e.loopDepth,
261 }
262 if loc.isName(ir.PPARAM) {
263 loc.param = true
264 } else if loc.isName(ir.PPARAMOUT) {
265 loc.paramOut = true
266 }
267
268 if persists {
269 loc.attrs |= attrPersists
270 }
271 e.allLocs = append(e.allLocs, loc)
272 if n != nil {
273 if n.Op() == ir.ONAME {
274 n := n.(*ir.Name)
275 if n.Class == ir.PPARAM && n.Curfn == nil {
276
277 } else if n.Curfn != e.curfn {
278 base.FatalfAt(n.Pos(), "curfn mismatch: %v != %v for %v", n.Curfn, e.curfn, n)
279 }
280
281 if n.Opt != nil {
282 base.FatalfAt(n.Pos(), "%v already has a location", n)
283 }
284 n.Opt = loc
285 }
286 }
287 return loc
288 }
289
290
291
292 func (e *escape) teeHole(ks ...hole) hole {
293 if len(ks) == 0 {
294 return e.discardHole()
295 }
296 if len(ks) == 1 {
297 return ks[0]
298 }
299
300
301
302
303
304 loc := e.newLoc(nil, false)
305 for _, k := range ks {
306
307
308
309
310
311 if k.derefs < 0 {
312 base.Fatalf("teeHole: negative derefs")
313 }
314
315 e.flow(k, loc)
316 }
317 return loc.asHole()
318 }
319
320
321
322
323 func (e *escape) later(k hole) hole {
324 loc := e.newLoc(nil, true)
325 e.flow(k, loc)
326 return loc.asHole()
327 }
328
329
330 func Fmt(n ir.Node) string {
331 text := ""
332 switch n.Esc() {
333 case ir.EscUnknown:
334 break
335
336 case ir.EscHeap:
337 text = "esc(h)"
338
339 case ir.EscNone:
340 text = "esc(no)"
341
342 case ir.EscNever:
343 text = "esc(N)"
344
345 default:
346 text = fmt.Sprintf("esc(%d)", n.Esc())
347 }
348
349 if n.Op() == ir.ONAME {
350 n := n.(*ir.Name)
351 if loc, ok := n.Opt.(*location); ok && loc.loopDepth != 0 {
352 if text != "" {
353 text += " "
354 }
355 text += fmt.Sprintf("ld(%d)", loc.loopDepth)
356 }
357 }
358
359 return text
360 }
361
View as plain text