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 queued bool
68
69
70
71
72 escapes bool
73
74
75
76
77 transient bool
78
79
80 paramEsc leaks
81
82 captured bool
83 reassigned bool
84 addrtaken bool
85 }
86
87
88 type edge struct {
89 src *location
90 derefs int
91 notes *note
92 }
93
94 func (l *location) asHole() hole {
95 return hole{dst: l}
96 }
97
98
99 func (l *location) leakTo(sink *location, derefs int) {
100
101
102
103 if !sink.escapes && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn {
104 ri := sink.resultIndex - 1
105 if ri < numEscResults {
106
107 l.paramEsc.AddResult(ri, derefs)
108 return
109 }
110 }
111
112
113 l.paramEsc.AddHeap(derefs)
114 }
115
116 func (l *location) isName(c ir.Class) bool {
117 return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class == c
118 }
119
120
121
122
123 type hole struct {
124 dst *location
125 derefs int
126 notes *note
127
128
129
130
131 addrtaken bool
132 }
133
134 type note struct {
135 next *note
136 where ir.Node
137 why string
138 }
139
140 func (k hole) note(where ir.Node, why string) hole {
141 if where == nil || why == "" {
142 base.Fatalf("note: missing where/why")
143 }
144 if base.Flag.LowerM >= 2 || logopt.Enabled() {
145 k.notes = ¬e{
146 next: k.notes,
147 where: where,
148 why: why,
149 }
150 }
151 return k
152 }
153
154 func (k hole) shift(delta int) hole {
155 k.derefs += delta
156 if k.derefs < -1 {
157 base.Fatalf("derefs underflow: %v", k.derefs)
158 }
159 k.addrtaken = delta < 0
160 return k
161 }
162
163 func (k hole) deref(where ir.Node, why string) hole { return k.shift(1).note(where, why) }
164 func (k hole) addr(where ir.Node, why string) hole { return k.shift(-1).note(where, why) }
165
166 func (k hole) dotType(t *types.Type, where ir.Node, why string) hole {
167 if !t.IsInterface() && !types.IsDirectIface(t) {
168 k = k.shift(1)
169 }
170 return k.note(where, why)
171 }
172
173 func (b *batch) flow(k hole, src *location) {
174 if k.addrtaken {
175 src.addrtaken = true
176 }
177
178 dst := k.dst
179 if dst == &b.blankLoc {
180 return
181 }
182 if dst == src && k.derefs >= 0 {
183 return
184 }
185 if dst.escapes && k.derefs < 0 {
186 if base.Flag.LowerM >= 2 || logopt.Enabled() {
187 pos := base.FmtPos(src.n.Pos())
188 if base.Flag.LowerM >= 2 {
189 fmt.Printf("%s: %v escapes to heap:\n", pos, src.n)
190 }
191 explanation := b.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{})
192 if logopt.Enabled() {
193 var e_curfn *ir.Func
194 logopt.LogOpt(src.n.Pos(), "escapes", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", src.n), explanation)
195 }
196
197 }
198 src.escapes = true
199 return
200 }
201
202
203 dst.edges = append(dst.edges, edge{src: src, derefs: k.derefs, notes: k.notes})
204 }
205
206 func (b *batch) heapHole() hole { return b.heapLoc.asHole() }
207 func (b *batch) discardHole() hole { return b.blankLoc.asHole() }
208
209 func (b *batch) oldLoc(n *ir.Name) *location {
210 if n.Canonical().Opt == nil {
211 base.Fatalf("%v has no location", n)
212 }
213 return n.Canonical().Opt.(*location)
214 }
215
216 func (e *escape) newLoc(n ir.Node, transient bool) *location {
217 if e.curfn == nil {
218 base.Fatalf("e.curfn isn't set")
219 }
220 if n != nil && n.Type() != nil && n.Type().NotInHeap() {
221 base.ErrorfAt(n.Pos(), "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type())
222 }
223
224 if n != nil && n.Op() == ir.ONAME {
225 if canon := n.(*ir.Name).Canonical(); n != canon {
226 base.Fatalf("newLoc on non-canonical %v (canonical is %v)", n, canon)
227 }
228 }
229 loc := &location{
230 n: n,
231 curfn: e.curfn,
232 loopDepth: e.loopDepth,
233 transient: transient,
234 }
235 e.allLocs = append(e.allLocs, loc)
236 if n != nil {
237 if n.Op() == ir.ONAME {
238 n := n.(*ir.Name)
239 if n.Class == ir.PPARAM && n.Curfn == nil {
240
241 } else if n.Curfn != e.curfn {
242 base.Fatalf("curfn mismatch: %v != %v for %v", n.Curfn, e.curfn, n)
243 }
244
245 if n.Opt != nil {
246 base.Fatalf("%v already has a location", n)
247 }
248 n.Opt = loc
249 }
250 }
251 return loc
252 }
253
254
255
256 func (e *escape) teeHole(ks ...hole) hole {
257 if len(ks) == 0 {
258 return e.discardHole()
259 }
260 if len(ks) == 1 {
261 return ks[0]
262 }
263
264
265
266
267
268 loc := e.newLoc(nil, true)
269 for _, k := range ks {
270
271
272
273
274
275 if k.derefs < 0 {
276 base.Fatalf("teeHole: negative derefs")
277 }
278
279 e.flow(k, loc)
280 }
281 return loc.asHole()
282 }
283
284
285
286
287 func (e *escape) later(k hole) hole {
288 loc := e.newLoc(nil, false)
289 e.flow(k, loc)
290 return loc.asHole()
291 }
292
293
294 func Fmt(n ir.Node) string {
295 text := ""
296 switch n.Esc() {
297 case ir.EscUnknown:
298 break
299
300 case ir.EscHeap:
301 text = "esc(h)"
302
303 case ir.EscNone:
304 text = "esc(no)"
305
306 case ir.EscNever:
307 text = "esc(N)"
308
309 default:
310 text = fmt.Sprintf("esc(%d)", n.Esc())
311 }
312
313 if n.Op() == ir.ONAME {
314 n := n.(*ir.Name)
315 if loc, ok := n.Opt.(*location); ok && loc.loopDepth != 0 {
316 if text != "" {
317 text += " "
318 }
319 text += fmt.Sprintf("ld(%d)", loc.loopDepth)
320 }
321 }
322
323 return text
324 }
325
View as plain text