1
2
3
4
5 package escape
6
7 import (
8 "fmt"
9 "go/constant"
10 "go/token"
11 "internal/goexperiment"
12
13 "cmd/compile/internal/base"
14 "cmd/compile/internal/ir"
15 "cmd/compile/internal/logopt"
16 "cmd/compile/internal/typecheck"
17 "cmd/compile/internal/types"
18 "cmd/internal/src"
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 type batch struct {
92 allLocs []*location
93 closures []closure
94 reassignOracles map[*ir.Func]*ir.ReassignOracle
95
96 heapLoc location
97 mutatorLoc location
98 calleeLoc location
99 blankLoc location
100 }
101
102
103
104 type closure struct {
105 k hole
106 clo *ir.ClosureExpr
107 }
108
109
110
111 type escape struct {
112 *batch
113
114 curfn *ir.Func
115
116 labels map[*types.Sym]labelState
117
118
119
120
121
122 loopDepth int
123 }
124
125 func Funcs(all []*ir.Func) {
126
127
128
129 reassignOracles := make(map[*ir.Func]*ir.ReassignOracle)
130
131 ir.VisitFuncsBottomUp(all, func(list []*ir.Func, recursive bool) {
132 Batch(list, reassignOracles)
133 })
134 }
135
136
137
138 func Batch(fns []*ir.Func, reassignOracles map[*ir.Func]*ir.ReassignOracle) {
139 var b batch
140 b.heapLoc.attrs = attrEscapes | attrPersists | attrMutates | attrCalls
141 b.mutatorLoc.attrs = attrMutates
142 b.calleeLoc.attrs = attrCalls
143 b.reassignOracles = reassignOracles
144
145
146 for _, fn := range fns {
147 if base.Flag.W > 1 {
148 s := fmt.Sprintf("\nbefore escape %v", fn)
149 ir.Dump(s, fn)
150 }
151 b.initFunc(fn)
152 }
153 for _, fn := range fns {
154 if !fn.IsClosure() {
155 b.walkFunc(fn)
156 }
157 }
158
159
160
161
162
163 for _, closure := range b.closures {
164 b.flowClosure(closure.k, closure.clo)
165 }
166 b.closures = nil
167
168 for _, loc := range b.allLocs {
169
170 b.rewriteWithLiterals(loc.n, loc.curfn)
171
172
173
174 if why := HeapAllocReason(loc.n); why != "" {
175 b.flow(b.heapHole().addr(loc.n, why), loc)
176 }
177 }
178
179 b.walkAll()
180 b.finish(fns)
181 }
182
183 func (b *batch) with(fn *ir.Func) *escape {
184 return &escape{
185 batch: b,
186 curfn: fn,
187 loopDepth: 1,
188 }
189 }
190
191 func (b *batch) initFunc(fn *ir.Func) {
192 e := b.with(fn)
193 if fn.Esc() != escFuncUnknown {
194 base.Fatalf("unexpected node: %v", fn)
195 }
196 fn.SetEsc(escFuncPlanned)
197 if base.Flag.LowerM > 3 {
198 ir.Dump("escAnalyze", fn)
199 }
200
201
202 for _, n := range fn.Dcl {
203 e.newLoc(n, true)
204 }
205
206
207
208 if fn.OClosure == nil {
209 for _, n := range fn.ClosureVars {
210 e.newLoc(n.Canonical(), true)
211 }
212 }
213
214
215 for i, f := range fn.Type().Results() {
216 e.oldLoc(f.Nname.(*ir.Name)).resultIndex = 1 + i
217 }
218 }
219
220 func (b *batch) walkFunc(fn *ir.Func) {
221 e := b.with(fn)
222 fn.SetEsc(escFuncStarted)
223
224
225 ir.Visit(fn, func(n ir.Node) {
226 switch n.Op() {
227 case ir.OLABEL:
228 n := n.(*ir.LabelStmt)
229 if n.Label.IsBlank() {
230 break
231 }
232 if e.labels == nil {
233 e.labels = make(map[*types.Sym]labelState)
234 }
235 e.labels[n.Label] = nonlooping
236
237 case ir.OGOTO:
238
239
240 n := n.(*ir.BranchStmt)
241 if e.labels[n.Label] == nonlooping {
242 e.labels[n.Label] = looping
243 }
244 }
245 })
246
247 e.block(fn.Body)
248
249 if len(e.labels) != 0 {
250 base.FatalfAt(fn.Pos(), "leftover labels after walkFunc")
251 }
252 }
253
254 func (b *batch) flowClosure(k hole, clo *ir.ClosureExpr) {
255 for _, cv := range clo.Func.ClosureVars {
256 n := cv.Canonical()
257 loc := b.oldLoc(cv)
258 if !loc.captured {
259 base.FatalfAt(cv.Pos(), "closure variable never captured: %v", cv)
260 }
261
262
263 n.SetByval(!loc.addrtaken && !loc.reassigned && n.Type().Size() <= 128)
264 if !n.Byval() {
265 n.SetAddrtaken(true)
266 if n.Sym().Name == typecheck.LocalDictName {
267 base.FatalfAt(n.Pos(), "dictionary variable not captured by value")
268 }
269 }
270
271 if base.Flag.LowerM > 1 {
272 how := "ref"
273 if n.Byval() {
274 how = "value"
275 }
276 base.WarnfAt(n.Pos(), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", n.Curfn, how, n, loc.addrtaken, loc.reassigned, n.Type().Size())
277 }
278
279
280 k := k
281 if !cv.Byval() {
282 k = k.addr(cv, "reference")
283 }
284 b.flow(k.note(cv, "captured by a closure"), loc)
285 }
286 }
287
288 func (b *batch) finish(fns []*ir.Func) {
289
290 for _, fn := range fns {
291 fn.SetEsc(escFuncTagged)
292
293 for i, param := range fn.Type().RecvParams() {
294 param.Note = b.paramTag(fn, 1+i, param)
295 }
296 }
297
298 for _, loc := range b.allLocs {
299 n := loc.n
300 if n == nil {
301 continue
302 }
303
304 if n.Op() == ir.ONAME {
305 n := n.(*ir.Name)
306 n.Opt = nil
307 }
308
309
310
311
312
313
314 goDeferWrapper := n.Op() == ir.OCLOSURE && n.(*ir.ClosureExpr).Func.Wrapper()
315
316 if loc.hasAttr(attrEscapes) {
317 if n.Op() == ir.ONAME {
318 if base.Flag.CompilingRuntime {
319 base.ErrorfAt(n.Pos(), 0, "%v escapes to heap, not allowed in runtime", n)
320 }
321 if base.Flag.LowerM != 0 {
322 base.WarnfAt(n.Pos(), "moved to heap: %v", n)
323 }
324 } else {
325 if base.Flag.LowerM != 0 && !goDeferWrapper {
326 if n.Op() == ir.OAPPEND {
327 base.WarnfAt(n.Pos(), "append escapes to heap")
328 } else {
329 base.WarnfAt(n.Pos(), "%v escapes to heap", n)
330 }
331 }
332 if logopt.Enabled() {
333 var e_curfn *ir.Func
334 logopt.LogOpt(n.Pos(), "escape", "escape", ir.FuncName(e_curfn))
335 }
336 }
337 n.SetEsc(ir.EscHeap)
338 } else {
339 if base.Flag.LowerM != 0 && n.Op() != ir.ONAME && !goDeferWrapper {
340 if n.Op() == ir.OAPPEND {
341 base.WarnfAt(n.Pos(), "append does not escape")
342 } else {
343 base.WarnfAt(n.Pos(), "%v does not escape", n)
344 }
345 }
346 n.SetEsc(ir.EscNone)
347 if !loc.hasAttr(attrPersists) {
348 switch n.Op() {
349 case ir.OCLOSURE:
350 n := n.(*ir.ClosureExpr)
351 n.SetTransient(true)
352 case ir.OMETHVALUE:
353 n := n.(*ir.SelectorExpr)
354 n.SetTransient(true)
355 case ir.OSLICELIT:
356 n := n.(*ir.CompLitExpr)
357 n.SetTransient(true)
358 }
359 }
360 }
361
362
363
364 if base.Debug.ZeroCopy != 0 {
365 if n, ok := n.(*ir.ConvExpr); ok && n.Op() == ir.OSTR2BYTES && !loc.hasAttr(attrMutates) {
366 if base.Flag.LowerM >= 1 {
367 base.WarnfAt(n.Pos(), "zero-copy string->[]byte conversion")
368 }
369 n.SetOp(ir.OSTR2BYTESTMP)
370 }
371 }
372 }
373
374 if goexperiment.RuntimeFreegc {
375
376
377 for _, fn := range fns {
378 a := aliasAnalysis{}
379 a.analyze(fn)
380 }
381 }
382
383 for _, fn := range fns {
384 if ir.MatchAstDump(fn, "escape") {
385 ir.AstDump(fn, "escape, "+ir.FuncName(fn))
386 }
387 }
388 }
389
390
391
392
393
394
395 func (b *batch) inMutualBatch(fn *ir.Name) bool {
396 if fn.Defn != nil && fn.Defn.Esc() < escFuncTagged {
397 if fn.Defn.Esc() == escFuncUnknown {
398 base.FatalfAt(fn.Pos(), "graph inconsistency: %v", fn)
399 }
400 return true
401 }
402 return false
403 }
404
405 const (
406 escFuncUnknown = 0 + iota
407 escFuncPlanned
408 escFuncStarted
409 escFuncTagged
410 )
411
412
413 type labelState int
414
415 const (
416 looping labelState = 1 + iota
417 nonlooping
418 )
419
420 func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string {
421 name := func() string {
422 if f.Nname != nil {
423 return f.Nname.Sym().Name
424 }
425 return fmt.Sprintf("arg#%d", narg)
426 }
427
428
429
430
431 diagnose := base.Flag.LowerM != 0 && !(fn.Wrapper() || fn.Dupok())
432
433 if len(fn.Body) == 0 {
434
435
436
437
438
439
440 fn.Pragma |= ir.UintptrKeepAlive
441
442 if f.Type.IsUintptr() {
443 if diagnose {
444 base.WarnfAt(f.Pos, "assuming %v is unsafe uintptr", name())
445 }
446 return ""
447 }
448
449 if !f.Type.HasPointers() {
450 return ""
451 }
452
453 var esc leaks
454
455
456
457 if fn.Pragma&ir.Noescape != 0 {
458 if diagnose && f.Sym != nil {
459 base.WarnfAt(f.Pos, "%v does not escape", name())
460 }
461 esc.AddMutator(0)
462 esc.AddCallee(0)
463 } else {
464 if diagnose && f.Sym != nil {
465 base.WarnfAt(f.Pos, "leaking param: %v", name())
466 }
467 esc.AddHeap(0)
468 }
469
470 return esc.Encode()
471 }
472
473 if fn.Pragma&ir.UintptrEscapes != 0 {
474 if f.Type.IsUintptr() {
475 if diagnose {
476 base.WarnfAt(f.Pos, "marking %v as escaping uintptr", name())
477 }
478 return ""
479 }
480 if f.IsDDD() && f.Type.Elem().IsUintptr() {
481
482 if diagnose {
483 base.WarnfAt(f.Pos, "marking %v as escaping ...uintptr", name())
484 }
485 return ""
486 }
487 }
488
489 if !f.Type.HasPointers() {
490 return ""
491 }
492
493
494 if f.Sym == nil || f.Sym.IsBlank() {
495 var esc leaks
496 return esc.Encode()
497 }
498
499 n := f.Nname.(*ir.Name)
500 loc := b.oldLoc(n)
501 esc := loc.paramEsc
502 esc.Optimize()
503
504 if diagnose && !loc.hasAttr(attrEscapes) {
505 b.reportLeaks(f.Pos, name(), esc, fn.Type())
506 }
507
508 return esc.Encode()
509 }
510
511 func (b *batch) reportLeaks(pos src.XPos, name string, esc leaks, sig *types.Type) {
512 warned := false
513 if x := esc.Heap(); x >= 0 {
514 if x == 0 {
515 base.WarnfAt(pos, "leaking param: %v", name)
516 } else {
517
518 base.WarnfAt(pos, "leaking param content: %v", name)
519 }
520 warned = true
521 }
522 for i := 0; i < numEscResults; i++ {
523 if x := esc.Result(i); x >= 0 {
524 res := sig.Result(i).Nname.Sym().Name
525 base.WarnfAt(pos, "leaking param: %v to result %v level=%d", name, res, x)
526 warned = true
527 }
528 }
529
530 if base.Debug.EscapeMutationsCalls <= 0 {
531 if !warned {
532 base.WarnfAt(pos, "%v does not escape", name)
533 }
534 return
535 }
536
537 if x := esc.Mutator(); x >= 0 {
538 base.WarnfAt(pos, "mutates param: %v derefs=%v", name, x)
539 warned = true
540 }
541 if x := esc.Callee(); x >= 0 {
542 base.WarnfAt(pos, "calls param: %v derefs=%v", name, x)
543 warned = true
544 }
545
546 if !warned {
547 base.WarnfAt(pos, "%v does not escape, mutate, or call", name)
548 }
549 }
550
551
552
553 func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
554 if n == nil || fn == nil {
555 return
556 }
557
558 assignTemp := func(pos src.XPos, n ir.Node, init *ir.Nodes) {
559
560 tmp := typecheck.TempAt(pos, fn, n.Type())
561 init.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)))
562 init.Append(typecheck.Stmt(ir.NewAssignStmt(pos, tmp, n)))
563 }
564
565 switch n.Op() {
566 case ir.OMAKESLICE:
567
568
569 n := n.(*ir.MakeExpr)
570
571 r := &n.Cap
572 if n.Cap == nil {
573 r = &n.Len
574 }
575
576 if (*r).Op() != ir.OLITERAL {
577
578 ro := b.reassignOracle(fn)
579 if ro == nil {
580 base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
581 }
582
583 s := ro.StaticValue(*r)
584 switch s.Op() {
585 case ir.OLITERAL:
586 lit, ok := s.(*ir.BasicLit)
587 if !ok || lit.Val().Kind() != constant.Int {
588 base.Fatalf("unexpected BasicLit Kind")
589 }
590 if constant.Compare(lit.Val(), token.GEQ, constant.MakeInt64(0)) {
591 if !base.LiteralAllocHash.MatchPos(n.Pos(), nil) {
592
593 return
594 }
595
596 assignTemp(n.Pos(), *r, n.PtrInit())
597 *r = ir.NewBasicLit(n.Pos(), (*r).Type(), lit.Val())
598 }
599 case ir.OLEN:
600 x := ro.StaticValue(s.(*ir.UnaryExpr).X)
601 if x.Op() == ir.OSLICELIT {
602 x := x.(*ir.CompLitExpr)
603
604 assignTemp(n.Pos(), *r, n.PtrInit())
605 *r = ir.NewBasicLit(n.Pos(), types.Types[types.TINT], constant.MakeInt64(x.Len))
606 }
607 }
608 }
609 case ir.OCONVIFACE:
610
611
612 conv := n.(*ir.ConvExpr)
613 if conv.X.Op() != ir.OLITERAL && !conv.X.Type().IsInterface() {
614
615
616 ro := b.reassignOracle(fn)
617 if ro == nil {
618 base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
619 }
620 v := ro.StaticValue(conv.X)
621 if v != nil && v.Op() == ir.OLITERAL && ir.ValidTypeForConst(conv.X.Type(), v.Val()) {
622 if !base.LiteralAllocHash.MatchPos(n.Pos(), nil) {
623
624 return
625 }
626 if base.Debug.EscapeDebug >= 3 {
627 base.WarnfAt(n.Pos(), "rewriting OCONVIFACE value from %v (%v) to %v (%v)", conv.X, conv.X.Type(), v, v.Type())
628 }
629
630 assignTemp(conv.Pos(), conv.X, conv.PtrInit())
631 v := v.(*ir.BasicLit)
632 conv.X = ir.NewBasicLit(conv.Pos(), conv.X.Type(), v.Val())
633 typecheck.Expr(conv)
634 }
635 }
636 }
637 }
638
639
640
641
642
643
644 func (b *batch) reassignOracle(fn *ir.Func) *ir.ReassignOracle {
645 if ro, ok := b.reassignOracles[fn]; ok {
646 return ro
647 }
648
649
650
651 f := fn
652 for f.ClosureParent != nil && !f.ClosureParent.IsPackageInit() {
653 f = f.ClosureParent
654 }
655
656 if f != fn {
657
658 ro := b.reassignOracles[f]
659 if ro != nil {
660
661 b.reassignOracles[fn] = ro
662 return ro
663 }
664 }
665
666
667 ro := &ir.ReassignOracle{}
668 ro.Init(f)
669
670
671 b.reassignOracles[fn] = ro
672 if f != fn {
673
674 b.reassignOracles[f] = ro
675 }
676 return ro
677 }
678
View as plain text