1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
8 "cmd/compile/internal/base"
9 "cmd/compile/internal/ir"
10 "cmd/compile/internal/logopt"
11 "cmd/compile/internal/types"
15 // Below we implement the methods for walking the AST and recording
16 // data flow edges. Note that because a sub-expression might have
17 // side-effects, it's important to always visit the entire AST.
19 // For example, write either:
30 // k = e.discardHole()
36 // // BAD: possibly loses side-effects within n.Left
41 // An location represents an abstract location that stores a Go
43 type location struct {
44 n ir.Node // represented variable or expression, if any
45 curfn *ir.Func // enclosing function
46 edges []edge // incoming edges
47 loopDepth int // loopDepth at declaration
49 // resultIndex records the tuple index (starting at 1) for
50 // PPARAMOUT variables within their function's result type.
51 // For non-PPARAMOUT variables it's 0.
54 // derefs and walkgen are used during walkOne to track the
55 // minimal dereferences from the walk root.
59 // dst and dstEdgeindex track the next immediate assignment
60 // destination location during walkone, along with the index
61 // of the edge pointing back to this location.
65 // queued is used by walkAll to track whether this location is
69 // attrs is a bitset of location attributes.
72 // paramEsc records the represented parameter's leak set.
75 captured bool // has a closure captured this variable?
76 reassigned bool // has this variable been reassigned?
77 addrtaken bool // has this variable's address been taken?
83 // attrEscapes indicates whether the represented variable's address
84 // escapes; that is, whether the variable must be heap allocated.
85 attrEscapes locAttr = 1 << iota
87 // attrPersists indicates whether the represented expression's
88 // address outlives the statement; that is, whether its storage
89 // cannot be immediately reused.
93 func (l *location) hasAttr(attr locAttr) bool { return l.attrs&attr != 0 }
95 // An edge represents an assignment edge between two Go variables.
102 func (l *location) asHole() hole {
106 // leak records that parameter l leaks to sink.
107 func (l *location) leakTo(sink *location, derefs int) {
108 // If sink is a result parameter that doesn't escape (#44614)
109 // and we can fit return bits into the escape analysis tag,
110 // then record as a result leak.
111 if !sink.hasAttr(attrEscapes) && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn {
112 ri := sink.resultIndex - 1
113 if ri < numEscResults {
114 // Leak to result parameter.
115 l.paramEsc.AddResult(ri, derefs)
120 // Otherwise, record as heap leak.
121 l.paramEsc.AddHeap(derefs)
124 func (l *location) isName(c ir.Class) bool {
125 return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class == c
128 // A hole represents a context for evaluation of a Go
129 // expression. E.g., when evaluating p in "x = **p", we'd have a hole
130 // with dst==x and derefs==2.
136 // addrtaken indicates whether this context is taking the address of
137 // the expression, independent of whether the address will actually
138 // be stored into a variable.
148 func (k hole) note(where ir.Node, why string) hole {
149 if where == nil || why == "" {
150 base.Fatalf("note: missing where/why")
152 if base.Flag.LowerM >= 2 || logopt.Enabled() {
162 func (k hole) shift(delta int) hole {
165 base.Fatalf("derefs underflow: %v", k.derefs)
167 k.addrtaken = delta < 0
171 func (k hole) deref(where ir.Node, why string) hole { return k.shift(1).note(where, why) }
172 func (k hole) addr(where ir.Node, why string) hole { return k.shift(-1).note(where, why) }
174 func (k hole) dotType(t *types.Type, where ir.Node, why string) hole {
175 if !t.IsInterface() && !types.IsDirectIface(t) {
178 return k.note(where, why)
181 func (b *batch) flow(k hole, src *location) {
187 if dst == &b.blankLoc {
190 if dst == src && k.derefs >= 0 { // dst = dst, dst = *dst, ...
193 if dst.hasAttr(attrEscapes) && k.derefs < 0 { // dst = &src
194 if base.Flag.LowerM >= 2 || logopt.Enabled() {
195 pos := base.FmtPos(src.n.Pos())
196 if base.Flag.LowerM >= 2 {
197 fmt.Printf("%s: %v escapes to heap:\n", pos, src.n)
199 explanation := b.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{})
200 if logopt.Enabled() {
201 var e_curfn *ir.Func // TODO(mdempsky): Fix.
202 logopt.LogOpt(src.n.Pos(), "escapes", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", src.n), explanation)
206 src.attrs |= attrEscapes
210 // TODO(mdempsky): Deduplicate edges?
211 dst.edges = append(dst.edges, edge{src: src, derefs: k.derefs, notes: k.notes})
214 func (b *batch) heapHole() hole { return b.heapLoc.asHole() }
215 func (b *batch) discardHole() hole { return b.blankLoc.asHole() }
217 func (b *batch) oldLoc(n *ir.Name) *location {
218 if n.Canonical().Opt == nil {
219 base.Fatalf("%v has no location", n)
221 return n.Canonical().Opt.(*location)
224 func (e *escape) newLoc(n ir.Node, persists bool) *location {
226 base.Fatalf("e.curfn isn't set")
228 if n != nil && n.Type() != nil && n.Type().NotInHeap() {
229 base.ErrorfAt(n.Pos(), 0, "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type())
232 if n != nil && n.Op() == ir.ONAME {
233 if canon := n.(*ir.Name).Canonical(); n != canon {
234 base.Fatalf("newLoc on non-canonical %v (canonical is %v)", n, canon)
240 loopDepth: e.loopDepth,
243 loc.attrs |= attrPersists
245 e.allLocs = append(e.allLocs, loc)
247 if n.Op() == ir.ONAME {
249 if n.Class == ir.PPARAM && n.Curfn == nil {
250 // ok; hidden parameter
251 } else if n.Curfn != e.curfn {
252 base.Fatalf("curfn mismatch: %v != %v for %v", n.Curfn, e.curfn, n)
256 base.Fatalf("%v already has a location", n)
264 // teeHole returns a new hole that flows into each hole of ks,
265 // similar to the Unix tee(1) command.
266 func (e *escape) teeHole(ks ...hole) hole {
268 return e.discardHole()
273 // TODO(mdempsky): Optimize if there's only one non-discard hole?
275 // Given holes "l1 = _", "l2 = **_", "l3 = *_", ..., create a
276 // new temporary location ltmp, wire it into place, and return
277 // a hole for "ltmp = _".
278 loc := e.newLoc(nil, false)
279 for _, k := range ks {
280 // N.B., "p = &q" and "p = &tmp; tmp = q" are not
281 // semantically equivalent. To combine holes like "l1
282 // = _" and "l2 = &_", we'd need to wire them as "l1 =
283 // *ltmp" and "l2 = ltmp" and return "ltmp = &_"
286 base.Fatalf("teeHole: negative derefs")
294 // later returns a new hole that flows into k, but some time later.
295 // Its main effect is to prevent immediate reuse of temporary
296 // variables introduced during Order.
297 func (e *escape) later(k hole) hole {
298 loc := e.newLoc(nil, true)
303 // Fmt is called from node printing to print information about escape analysis results.
304 func Fmt(n ir.Node) string {
320 text = fmt.Sprintf("esc(%d)", n.Esc())
323 if n.Op() == ir.ONAME {
325 if loc, ok := n.Opt.(*location); ok && loc.loopDepth != 0 {
329 text += fmt.Sprintf("ld(%d)", loc.loopDepth)