Source file src/pkg/cmd/compile/internal/gc/escape.go
1
2
3
4
5 package gc
6
7 import (
8 "cmd/compile/internal/types"
9 "fmt"
10 )
11
12
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
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 type Escape struct {
81 allLocs []*EscLocation
82
83 curfn *Node
84
85
86
87
88
89 loopDepth int
90
91 heapLoc EscLocation
92 blankLoc EscLocation
93 }
94
95
96
97 type EscLocation struct {
98 n *Node
99 curfn *Node
100 edges []EscEdge
101 loopDepth int
102
103
104
105 derefs int
106 walkgen uint32
107
108
109
110
111 escapes bool
112
113
114
115
116 transient bool
117
118
119
120 paramEsc uint16
121 }
122
123
124 type EscEdge struct {
125 src *EscLocation
126 derefs int
127 }
128
129
130
131 func escapeFuncs(fns []*Node, recursive bool) {
132 for _, fn := range fns {
133 if fn.Op != ODCLFUNC {
134 Fatalf("unexpected node: %v", fn)
135 }
136 }
137
138 var e Escape
139
140
141 for _, fn := range fns {
142 e.initFunc(fn)
143 }
144 for _, fn := range fns {
145 e.walkFunc(fn)
146 }
147 e.curfn = nil
148
149 e.walkAll()
150 e.finish()
151
152
153 for _, fn := range fns {
154 esctag(fn)
155 }
156 }
157
158 func (e *Escape) initFunc(fn *Node) {
159 if fn.Op != ODCLFUNC || fn.Esc != EscFuncUnknown {
160 Fatalf("unexpected node: %v", fn)
161 }
162 fn.Esc = EscFuncPlanned
163 if Debug['m'] > 3 {
164 Dump("escAnalyze", fn)
165 }
166
167 e.curfn = fn
168 e.loopDepth = 1
169
170
171 for _, dcl := range fn.Func.Dcl {
172 if dcl.Op == ONAME {
173 loc := e.newLoc(dcl, false)
174
175 if dcl.Class() == PPARAM && fn.Nbody.Len() == 0 && !fn.Noescape() {
176 loc.paramEsc = EscHeap
177 }
178 }
179 }
180 }
181
182 func (e *Escape) walkFunc(fn *Node) {
183 fn.Esc = EscFuncStarted
184
185
186 inspectList(fn.Nbody, func(n *Node) bool {
187 switch n.Op {
188 case OLABEL:
189 n.Sym.Label = asTypesNode(&nonlooping)
190
191 case OGOTO:
192
193
194 if n.Sym.Label == asTypesNode(&nonlooping) {
195 n.Sym.Label = asTypesNode(&looping)
196 }
197 }
198
199 return true
200 })
201
202 e.curfn = fn
203 e.loopDepth = 1
204 e.stmts(fn.Nbody)
205 }
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234 func (e *Escape) stmt(n *Node) {
235 if n == nil {
236 return
237 }
238
239 lno := setlineno(n)
240 defer func() {
241 lineno = lno
242 }()
243
244 if Debug['m'] > 2 {
245 fmt.Printf("%v:[%d] %v stmt: %v\n", linestr(lineno), e.loopDepth, funcSym(e.curfn), n)
246 }
247
248 e.stmts(n.Ninit)
249
250 switch n.Op {
251 default:
252 Fatalf("unexpected stmt: %v", n)
253
254 case ODCLCONST, ODCLTYPE, OEMPTY, OFALL, OINLMARK:
255
256
257 case OBREAK, OCONTINUE, OGOTO:
258
259
260 case OBLOCK:
261 e.stmts(n.List)
262
263 case ODCL:
264
265 if !n.Left.isBlank() {
266 e.dcl(n.Left)
267 }
268
269 case OLABEL:
270 switch asNode(n.Sym.Label) {
271 case &nonlooping:
272 if Debug['m'] > 2 {
273 fmt.Printf("%v:%v non-looping label\n", linestr(lineno), n)
274 }
275 case &looping:
276 if Debug['m'] > 2 {
277 fmt.Printf("%v: %v looping label\n", linestr(lineno), n)
278 }
279 e.loopDepth++
280 default:
281 Fatalf("label missing tag")
282 }
283 n.Sym.Label = nil
284
285 case OIF:
286 e.discard(n.Left)
287 e.stmts(n.Nbody)
288 e.stmts(n.Rlist)
289
290 case OFOR, OFORUNTIL:
291 e.loopDepth++
292 e.discard(n.Left)
293 e.stmt(n.Right)
294 e.stmts(n.Nbody)
295 e.loopDepth--
296
297 case ORANGE:
298
299
300
301 tv := e.newLoc(n, false)
302 e.expr(tv.asHole(), n.Right)
303
304 e.loopDepth++
305 ks := e.addrs(n.List)
306 if len(ks) >= 2 {
307 if n.Right.Type.IsArray() {
308 e.flow(ks[1].note(n, "range"), tv)
309 } else {
310 e.flow(ks[1].deref(n, "range-deref"), tv)
311 }
312 }
313
314 e.stmts(n.Nbody)
315 e.loopDepth--
316
317 case OSWITCH:
318 var tv *EscLocation
319 if n.Left != nil {
320 if n.Left.Op == OTYPESW {
321 k := e.discardHole()
322 if n.Left.Left != nil {
323 tv = e.newLoc(n.Left, false)
324 k = tv.asHole()
325 }
326 e.expr(k, n.Left.Right)
327 } else {
328 e.discard(n.Left)
329 }
330 }
331
332 for _, cas := range n.List.Slice() {
333 if tv != nil {
334
335 cv := cas.Rlist.First()
336 k := e.dcl(cv)
337 if types.Haspointers(cv.Type) {
338 e.flow(k.dotType(cv.Type, n, "switch case"), tv)
339 }
340 }
341
342 e.discards(cas.List)
343 e.stmts(cas.Nbody)
344 }
345
346 case OSELECT:
347 for _, cas := range n.List.Slice() {
348 e.stmt(cas.Left)
349 e.stmts(cas.Nbody)
350 }
351 case OSELRECV:
352 e.assign(n.Left, n.Right, "selrecv", n)
353 case OSELRECV2:
354 e.assign(n.Left, n.Right, "selrecv", n)
355 e.assign(n.List.First(), nil, "selrecv", n)
356 case ORECV:
357
358 e.exprSkipInit(e.discardHole(), n)
359 case OSEND:
360 e.discard(n.Left)
361 e.assignHeap(n.Right, "send", n)
362
363 case OAS, OASOP:
364 e.assign(n.Left, n.Right, "assign", n)
365
366 case OAS2:
367 for i, nl := range n.List.Slice() {
368 e.assign(nl, n.Rlist.Index(i), "assign-pair", n)
369 }
370
371 case OAS2DOTTYPE:
372 e.assign(n.List.First(), n.Rlist.First(), "assign-pair-dot-type", n)
373 e.assign(n.List.Second(), nil, "assign-pair-dot-type", n)
374 case OAS2MAPR:
375 e.assign(n.List.First(), n.Rlist.First(), "assign-pair-mapr", n)
376 e.assign(n.List.Second(), nil, "assign-pair-mapr", n)
377 case OAS2RECV:
378 e.assign(n.List.First(), n.Rlist.First(), "assign-pair-receive", n)
379 e.assign(n.List.Second(), nil, "assign-pair-receive", n)
380
381 case OAS2FUNC:
382 e.stmts(n.Rlist.First().Ninit)
383 e.call(e.addrs(n.List), n.Rlist.First(), nil)
384 case ORETURN:
385 results := e.curfn.Type.Results().FieldSlice()
386 for i, v := range n.List.Slice() {
387 e.assign(asNode(results[i].Nname), v, "return", n)
388 }
389 case OCALLFUNC, OCALLMETH, OCALLINTER, OCLOSE, OCOPY, ODELETE, OPANIC, OPRINT, OPRINTN, ORECOVER:
390 e.call(nil, n, nil)
391 case OGO, ODEFER:
392 e.stmts(n.Left.Ninit)
393 e.call(nil, n.Left, n)
394
395 case ORETJMP:
396
397 }
398 }
399
400 func (e *Escape) stmts(l Nodes) {
401
402 for _, n := range l.Slice() {
403 e.stmt(n)
404 }
405 }
406
407
408
409 func (e *Escape) expr(k EscHole, n *Node) {
410 if n == nil {
411 return
412 }
413 e.stmts(n.Ninit)
414 e.exprSkipInit(k, n)
415 }
416
417 func (e *Escape) exprSkipInit(k EscHole, n *Node) {
418 if n == nil {
419 return
420 }
421
422 lno := setlineno(n)
423 defer func() {
424 lineno = lno
425 }()
426
427 if k.derefs >= 0 && !types.Haspointers(n.Type) {
428 k = e.discardHole()
429 }
430
431 switch n.Op {
432 default:
433 Fatalf("unexpected expr: %v", n)
434
435 case OLITERAL, OGETG, OCLOSUREVAR, OTYPE:
436
437
438 case ONAME:
439 if n.Class() == PFUNC || n.Class() == PEXTERN {
440 return
441 }
442 e.flow(k, e.oldLoc(n))
443
444 case OPLUS, ONEG, OBITNOT, ONOT:
445 e.discard(n.Left)
446 case OADD, OSUB, OOR, OXOR, OMUL, ODIV, OMOD, OLSH, ORSH, OAND, OANDNOT, OEQ, ONE, OLT, OLE, OGT, OGE, OANDAND, OOROR:
447 e.discard(n.Left)
448 e.discard(n.Right)
449
450 case OADDR:
451 e.expr(k.addr(n, "address-of"), n.Left)
452 case ODEREF:
453 e.expr(k.deref(n, "indirection"), n.Left)
454 case ODOT, ODOTMETH, ODOTINTER:
455 e.expr(k.note(n, "dot"), n.Left)
456 case ODOTPTR:
457 e.expr(k.deref(n, "dot of pointer"), n.Left)
458 case ODOTTYPE, ODOTTYPE2:
459 e.expr(k.dotType(n.Type, n, "dot"), n.Left)
460 case OINDEX:
461 if n.Left.Type.IsArray() {
462 e.expr(k.note(n, "fixed-array-index-of"), n.Left)
463 } else {
464
465 e.expr(k.deref(n, "dot of pointer"), n.Left)
466 }
467 e.discard(n.Right)
468 case OINDEXMAP:
469 e.discard(n.Left)
470 e.discard(n.Right)
471 case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR:
472 e.expr(k.note(n, "slice"), n.Left)
473 low, high, max := n.SliceBounds()
474 e.discard(low)
475 e.discard(high)
476 e.discard(max)
477
478 case OCONV, OCONVNOP:
479 if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR {
480 e.unsafeValue(k, n.Left)
481 } else {
482 e.expr(k, n.Left)
483 }
484 case OCONVIFACE:
485 if !n.Left.Type.IsInterface() && !isdirectiface(n.Left.Type) {
486 k = e.spill(k, n)
487 } else {
488
489
490
491
492
493 _ = e.spill(k, n)
494 }
495 e.expr(k.note(n, "interface-converted"), n.Left)
496
497 case ORECV:
498 e.discard(n.Left)
499
500 case OCALLMETH, OCALLFUNC, OCALLINTER, OLEN, OCAP, OCOMPLEX, OREAL, OIMAG, OAPPEND, OCOPY:
501 e.call([]EscHole{k}, n, nil)
502
503 case ONEW:
504 e.spill(k, n)
505
506 case OMAKESLICE:
507 e.spill(k, n)
508 e.discard(n.Left)
509 e.discard(n.Right)
510 case OMAKECHAN:
511 e.discard(n.Left)
512 case OMAKEMAP:
513 e.spill(k, n)
514 e.discard(n.Left)
515
516 case ORECOVER:
517
518
519 case OCALLPART:
520 e.spill(k, n)
521
522
523
524
525
526 e.assignHeap(n.Left, "call part", n)
527
528 case OPTRLIT:
529 e.expr(e.spill(k, n), n.Left)
530
531 case OARRAYLIT:
532 for _, elt := range n.List.Slice() {
533 if elt.Op == OKEY {
534 elt = elt.Right
535 }
536 e.expr(k.note(n, "array literal element"), elt)
537 }
538
539 case OSLICELIT:
540 k = e.spill(k, n)
541
542 for _, elt := range n.List.Slice() {
543 if elt.Op == OKEY {
544 elt = elt.Right
545 }
546 e.expr(k.note(n, "slice-literal-element"), elt)
547 }
548
549 case OSTRUCTLIT:
550 for _, elt := range n.List.Slice() {
551 e.expr(k.note(n, "struct literal element"), elt.Left)
552 }
553
554 case OMAPLIT:
555 e.spill(k, n)
556
557
558 for _, elt := range n.List.Slice() {
559 e.assignHeap(elt.Left, "map literal key", n)
560 e.assignHeap(elt.Right, "map literal value", n)
561 }
562
563 case OCLOSURE:
564 k = e.spill(k, n)
565
566
567 for _, v := range n.Func.Closure.Func.Cvars.Slice() {
568 if v.Op == OXXX {
569 continue
570 }
571
572 k := k
573 if !v.Name.Byval() {
574 k = k.addr(v, "reference")
575 }
576
577 e.expr(k.note(n, "captured by a closure"), v.Name.Defn)
578 }
579
580 case ORUNES2STR, OBYTES2STR, OSTR2RUNES, OSTR2BYTES, ORUNESTR:
581 e.spill(k, n)
582 e.discard(n.Left)
583
584 case OADDSTR:
585 e.spill(k, n)
586
587
588
589 e.discards(n.List)
590 }
591 }
592
593
594
595 func (e *Escape) unsafeValue(k EscHole, n *Node) {
596 if n.Type.Etype != TUINTPTR {
597 Fatalf("unexpected type %v for %v", n.Type, n)
598 }
599
600 e.stmts(n.Ninit)
601
602 switch n.Op {
603 case OCONV, OCONVNOP:
604 if n.Left.Type.Etype == TUNSAFEPTR {
605 e.expr(k, n.Left)
606 } else {
607 e.discard(n.Left)
608 }
609 case ODOTPTR:
610 if isReflectHeaderDataField(n) {
611 e.expr(k.deref(n, "reflect.Header.Data"), n.Left)
612 } else {
613 e.discard(n.Left)
614 }
615 case OPLUS, ONEG, OBITNOT:
616 e.unsafeValue(k, n.Left)
617 case OADD, OSUB, OOR, OXOR, OMUL, ODIV, OMOD, OAND, OANDNOT:
618 e.unsafeValue(k, n.Left)
619 e.unsafeValue(k, n.Right)
620 case OLSH, ORSH:
621 e.unsafeValue(k, n.Left)
622
623
624 e.discard(n.Right)
625 default:
626 e.exprSkipInit(e.discardHole(), n)
627 }
628 }
629
630
631
632 func (e *Escape) discard(n *Node) {
633 e.expr(e.discardHole(), n)
634 }
635
636 func (e *Escape) discards(l Nodes) {
637 for _, n := range l.Slice() {
638 e.discard(n)
639 }
640 }
641
642
643
644 func (e *Escape) addr(n *Node) EscHole {
645 if n == nil || n.isBlank() {
646
647
648 return e.discardHole()
649 }
650
651 k := e.heapHole()
652
653 switch n.Op {
654 default:
655 Fatalf("unexpected addr: %v", n)
656 case ONAME:
657 if n.Class() == PEXTERN {
658 break
659 }
660 k = e.oldLoc(n).asHole()
661 case ODOT:
662 k = e.addr(n.Left)
663 case OINDEX:
664 e.discard(n.Right)
665 if n.Left.Type.IsArray() {
666 k = e.addr(n.Left)
667 } else {
668 e.discard(n.Left)
669 }
670 case ODEREF, ODOTPTR:
671 e.discard(n)
672 case OINDEXMAP:
673 e.discard(n.Left)
674 e.assignHeap(n.Right, "key of map put", n)
675 }
676
677 if !types.Haspointers(n.Type) {
678 k = e.discardHole()
679 }
680
681 return k
682 }
683
684 func (e *Escape) addrs(l Nodes) []EscHole {
685 var ks []EscHole
686 for _, n := range l.Slice() {
687 ks = append(ks, e.addr(n))
688 }
689 return ks
690 }
691
692
693 func (e *Escape) assign(dst, src *Node, why string, where *Node) {
694
695 ignore := dst != nil && src != nil && isSelfAssign(dst, src)
696 if ignore && Debug['m'] != 0 {
697 Warnl(where.Pos, "%v ignoring self-assignment in %S", funcSym(e.curfn), where)
698 }
699
700 k := e.addr(dst)
701 if dst != nil && dst.Op == ODOTPTR && isReflectHeaderDataField(dst) {
702 e.unsafeValue(e.heapHole(), src)
703 } else {
704 if ignore {
705 k = e.discardHole()
706 }
707 e.expr(k, src)
708 }
709 }
710
711 func (e *Escape) assignHeap(src *Node, why string, where *Node) {
712 e.expr(e.heapHole().note(where, why), src)
713 }
714
715
716
717
718 func (e *Escape) call(ks []EscHole, call, where *Node) {
719
720
721 var fn, recv *Node
722 var fntype *types.Type
723 args := call.List.Slice()
724 switch call.Op {
725 case OCALLFUNC:
726 fn = call.Left
727 if fn.Op == OCLOSURE {
728 fn = fn.Func.Closure.Func.Nname
729 }
730 fntype = fn.Type
731 case OCALLMETH:
732 fn = asNode(call.Left.Type.FuncType().Nname)
733 fntype = fn.Type
734 recv = call.Left.Left
735 case OCALLINTER:
736 fntype = call.Left.Type
737 recv = call.Left.Left
738 case OAPPEND, ODELETE, OPRINT, OPRINTN, ORECOVER:
739
740 case OLEN, OCAP, OREAL, OIMAG, OCLOSE, OPANIC:
741 args = []*Node{call.Left}
742 case OCOMPLEX, OCOPY:
743 args = []*Node{call.Left, call.Right}
744 default:
745 Fatalf("unexpected call op: %v", call.Op)
746 }
747
748 static := fn != nil && fn.Op == ONAME && fn.Class() == PFUNC
749
750
751 var recvK EscHole
752 var paramKs []EscHole
753
754 if static && fn.Name.Defn != nil && fn.Name.Defn.Esc < EscFuncTagged {
755
756
757
758 if fn.Name.Defn.Esc == EscFuncUnknown {
759 Fatalf("graph inconsistency")
760 }
761
762 if ks != nil {
763 for i, result := range fntype.Results().FieldSlice() {
764 e.expr(ks[i], asNode(result.Nname))
765 }
766 }
767
768 if r := fntype.Recv(); r != nil {
769 recvK = e.addr(asNode(r.Nname))
770 }
771 for _, param := range fntype.Params().FieldSlice() {
772 paramKs = append(paramKs, e.addr(asNode(param.Nname)))
773 }
774 } else if call.Op == OCALLFUNC || call.Op == OCALLMETH || call.Op == OCALLINTER {
775
776
777
778 if r := fntype.Recv(); r != nil {
779 recvK = e.tagHole(ks, r, static)
780 }
781 for _, param := range fntype.Params().FieldSlice() {
782 paramKs = append(paramKs, e.tagHole(ks, param, static))
783 }
784 } else {
785
786
787 for range args {
788 paramKs = append(paramKs, e.discardHole())
789 }
790
791 switch call.Op {
792 case OAPPEND:
793
794
795
796
797
798 paramKs[0] = e.teeHole(paramKs[0], ks[0])
799 if types.Haspointers(args[0].Type.Elem()) {
800 paramKs[0] = e.teeHole(paramKs[0], e.heapHole().deref(call, "appendee slice"))
801 }
802
803 if call.IsDDD() {
804 if args[1].Type.IsSlice() && types.Haspointers(args[1].Type.Elem()) {
805 paramKs[1] = e.teeHole(paramKs[1], e.heapHole().deref(call, "appended slice..."))
806 }
807 } else {
808 for i := 1; i < len(args); i++ {
809 paramKs[i] = e.heapHole()
810 }
811 }
812
813 case OCOPY:
814 if call.Right.Type.IsSlice() && types.Haspointers(call.Right.Type.Elem()) {
815 paramKs[1] = e.teeHole(paramKs[1], e.heapHole().deref(call, "copied slice"))
816 }
817
818 case OPANIC:
819 paramKs[0] = e.heapHole()
820 }
821 }
822
823 if call.Op == OCALLFUNC {
824
825 e.expr(e.augmentParamHole(e.discardHole(), where), call.Left)
826 }
827
828 if recv != nil {
829
830 e.expr(e.augmentParamHole(recvK, where), recv)
831 }
832
833
834
835 for i, paramK := range paramKs {
836 paramKs[i] = e.augmentParamHole(paramK, where)
837 }
838
839
840 if fntype != nil && fntype.IsVariadic() && !call.IsDDD() {
841 vi := fntype.NumParams() - 1
842
843 elt := fntype.Params().Field(vi).Type.Elem()
844 nva := call.List.Len()
845 nva -= vi
846
847
848 ddd := nodl(call.Pos, ODDDARG, nil, nil)
849 ddd.Type = types.NewPtr(types.NewArray(elt, int64(nva)))
850 call.Right = ddd
851
852 dddK := e.spill(paramKs[vi], ddd)
853 paramKs = paramKs[:vi]
854 for i := 0; i < nva; i++ {
855 paramKs = append(paramKs, dddK)
856 }
857 }
858
859 for i, arg := range args {
860
861
862
863 if static && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.Etype == TUNSAFEPTR {
864 x := i
865 if fntype.IsVariadic() && x >= fntype.NumParams() {
866 x = fntype.NumParams() - 1
867 }
868 if fntype.Params().Field(x).Note == uintptrEscapesTag {
869 arg = arg.Left
870 }
871 }
872
873
874 e.expr(paramKs[i], arg)
875 }
876 }
877
878
879
880 func (e *Escape) augmentParamHole(k EscHole, where *Node) EscHole {
881 if where == nil {
882 return k
883 }
884
885
886
887
888
889 if where.Op == ODEFER && e.loopDepth == 1 {
890 where.Esc = EscNever
891
892 return e.teeHole(k, e.newLoc(nil, false).asHole())
893 }
894
895 return e.heapHole()
896 }
897
898
899
900
901
902 func (e *Escape) tagHole(ks []EscHole, param *types.Field, static bool) EscHole {
903
904 if !static {
905 return e.heapHole()
906 }
907
908 esc := parsetag(param.Note)
909 switch esc {
910 case EscHeap, EscUnknown:
911 return e.heapHole()
912 }
913
914 var tagKs []EscHole
915 if esc&EscContentEscapes != 0 {
916 tagKs = append(tagKs, e.heapHole().shift(1))
917 }
918
919 if ks != nil {
920 for i := 0; i < numEscReturns; i++ {
921 if x := getEscReturn(esc, i); x >= 0 {
922 tagKs = append(tagKs, ks[i].shift(x))
923 }
924 }
925 }
926
927 return e.teeHole(tagKs...)
928 }
929
930
931
932
933 type EscHole struct {
934 dst *EscLocation
935 derefs int
936 }
937
938 func (k EscHole) note(where *Node, why string) EscHole {
939
940 return k
941 }
942
943 func (k EscHole) shift(delta int) EscHole {
944 k.derefs += delta
945 if k.derefs < -1 {
946 Fatalf("derefs underflow: %v", k.derefs)
947 }
948 return k
949 }
950
951 func (k EscHole) deref(where *Node, why string) EscHole { return k.shift(1).note(where, why) }
952 func (k EscHole) addr(where *Node, why string) EscHole { return k.shift(-1).note(where, why) }
953
954 func (k EscHole) dotType(t *types.Type, where *Node, why string) EscHole {
955 if !t.IsInterface() && !isdirectiface(t) {
956 k = k.shift(1)
957 }
958 return k.note(where, why)
959 }
960
961
962
963 func (e *Escape) teeHole(ks ...EscHole) EscHole {
964 if len(ks) == 0 {
965 return e.discardHole()
966 }
967 if len(ks) == 1 {
968 return ks[0]
969 }
970
971
972
973
974
975 loc := e.newLoc(nil, true)
976 for _, k := range ks {
977
978
979
980
981
982 if k.derefs < 0 {
983 Fatalf("teeHole: negative derefs")
984 }
985
986 e.flow(k, loc)
987 }
988 return loc.asHole()
989 }
990
991 func (e *Escape) dcl(n *Node) EscHole {
992 loc := e.oldLoc(n)
993 loc.loopDepth = e.loopDepth
994 return loc.asHole()
995 }
996
997 func (e *Escape) spill(k EscHole, n *Node) EscHole {
998
999
1000
1001 loc := e.newLoc(n, true)
1002 e.flow(k.addr(n, "spill"), loc)
1003 return loc.asHole()
1004 }
1005
1006
1007
1008 func canonicalNode(n *Node) *Node {
1009 if n != nil && n.IsClosureVar() {
1010 n = n.Name.Defn
1011 if n.IsClosureVar() {
1012 Fatalf("still closure var")
1013 }
1014 }
1015
1016 return n
1017 }
1018
1019 func (e *Escape) newLoc(n *Node, transient bool) *EscLocation {
1020 if e.curfn == nil {
1021 Fatalf("e.curfn isn't set")
1022 }
1023
1024 n = canonicalNode(n)
1025 loc := &EscLocation{
1026 n: n,
1027 curfn: e.curfn,
1028 loopDepth: e.loopDepth,
1029 transient: transient,
1030 }
1031 e.allLocs = append(e.allLocs, loc)
1032 if n != nil {
1033 if n.Op == ONAME && n.Name.Curfn != e.curfn {
1034 Fatalf("curfn mismatch: %v != %v", n.Name.Curfn, e.curfn)
1035 }
1036
1037 if n.HasOpt() {
1038 Fatalf("%v already has a location", n)
1039 }
1040 n.SetOpt(loc)
1041
1042
1043 if mustHeapAlloc(n) && !loc.isName(PPARAM) && !loc.isName(PPARAMOUT) {
1044 e.flow(e.heapHole().addr(nil, ""), loc)
1045 }
1046 }
1047 return loc
1048 }
1049
1050 func (e *Escape) oldLoc(n *Node) *EscLocation {
1051 n = canonicalNode(n)
1052 return n.Opt().(*EscLocation)
1053 }
1054
1055 func (l *EscLocation) asHole() EscHole {
1056 return EscHole{dst: l}
1057 }
1058
1059 func (e *Escape) flow(k EscHole, src *EscLocation) {
1060 dst := k.dst
1061 if dst == &e.blankLoc {
1062 return
1063 }
1064 if dst == src && k.derefs >= 0 {
1065 return
1066 }
1067
1068
1069
1070 dst.edges = append(dst.edges, EscEdge{src: src, derefs: k.derefs})
1071 }
1072
1073 func (e *Escape) heapHole() EscHole { return e.heapLoc.asHole() }
1074 func (e *Escape) discardHole() EscHole { return e.blankLoc.asHole() }
1075
1076
1077
1078 func (e *Escape) walkAll() {
1079 var walkgen uint32
1080
1081 for _, loc := range e.allLocs {
1082 walkgen++
1083 e.walkOne(loc, walkgen)
1084 }
1085
1086
1087
1088 walkgen++
1089 e.walkOne(&e.heapLoc, walkgen)
1090 }
1091
1092
1093
1094 func (e *Escape) walkOne(root *EscLocation, walkgen uint32) {
1095
1096
1097
1098
1099 root.walkgen = walkgen
1100 root.derefs = 0
1101
1102 todo := []*EscLocation{root}
1103 for len(todo) > 0 {
1104 l := todo[len(todo)-1]
1105 todo = todo[:len(todo)-1]
1106
1107 base := l.derefs
1108
1109
1110 addressOf := base < 0
1111 if addressOf {
1112
1113
1114
1115
1116 base = 0
1117
1118
1119
1120
1121 if !root.transient {
1122 l.transient = false
1123
1124 }
1125 }
1126
1127 if e.outlives(root, l) {
1128
1129
1130
1131 if addressOf && !l.escapes {
1132 l.escapes = true
1133
1134
1135
1136
1137
1138 if root != &e.heapLoc {
1139 e.flow(e.heapHole(), l)
1140 }
1141 }
1142
1143
1144
1145
1146
1147
1148 if l.isName(PPARAM) {
1149 l.leakTo(root, base)
1150 }
1151 }
1152
1153 for _, edge := range l.edges {
1154 derefs := base + edge.derefs
1155 if edge.src.walkgen != walkgen || edge.src.derefs > derefs {
1156 edge.src.walkgen = walkgen
1157 edge.src.derefs = derefs
1158 todo = append(todo, edge.src)
1159 }
1160 }
1161 }
1162 }
1163
1164
1165
1166 func (e *Escape) outlives(l, other *EscLocation) bool {
1167
1168 if l == &e.heapLoc {
1169 return true
1170 }
1171
1172
1173
1174
1175 if l.isName(PPARAMOUT) {
1176
1177
1178
1179
1180
1181
1182 if containsClosure(other.curfn, l.curfn) && l.curfn.Func.Closure.Func.Top&ctxCallee != 0 {
1183 return false
1184 }
1185
1186 return true
1187 }
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197 if l.curfn == other.curfn && l.loopDepth < other.loopDepth {
1198 return true
1199 }
1200
1201
1202
1203
1204
1205
1206
1207
1208 if containsClosure(l.curfn, other.curfn) {
1209 return true
1210 }
1211
1212 return false
1213 }
1214
1215
1216 func containsClosure(f, c *Node) bool {
1217 if f.Op != ODCLFUNC || c.Op != ODCLFUNC {
1218 Fatalf("bad containsClosure: %v, %v", f, c)
1219 }
1220
1221
1222 if f == c {
1223 return false
1224 }
1225
1226
1227
1228 fn := f.Func.Nname.Sym.Name
1229 cn := c.Func.Nname.Sym.Name
1230 return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.'
1231 }
1232
1233
1234 func (l *EscLocation) leakTo(sink *EscLocation, derefs int) {
1235
1236 if l.paramEsc == EscHeap {
1237 return
1238 }
1239
1240
1241
1242 if sink.isName(PPARAMOUT) && sink.curfn == l.curfn {
1243
1244 ri := int(sink.n.Name.Vargen) - 1
1245 if ri < numEscReturns {
1246
1247 if old := getEscReturn(l.paramEsc, ri); old < 0 || derefs < old {
1248 l.paramEsc = setEscReturn(l.paramEsc, ri, derefs)
1249 }
1250 return
1251 }
1252 }
1253
1254
1255 if derefs > 0 {
1256 l.paramEsc |= EscContentEscapes
1257 } else {
1258 l.paramEsc = EscHeap
1259 }
1260 }
1261
1262 func (e *Escape) finish() {
1263 for _, loc := range e.allLocs {
1264 n := loc.n
1265 if n == nil {
1266 continue
1267 }
1268 n.SetOpt(nil)
1269
1270
1271
1272
1273
1274
1275
1276
1277 if loc.escapes {
1278 if Debug['m'] != 0 && n.Op != ONAME {
1279 Warnl(n.Pos, "%S escapes to heap", n)
1280 }
1281 n.Esc = EscHeap
1282 addrescapes(n)
1283 } else if loc.isName(PPARAM) {
1284 n.Esc = finalizeEsc(loc.paramEsc)
1285
1286 if Debug['m'] != 0 && types.Haspointers(n.Type) {
1287 if n.Esc == EscNone {
1288 Warnl(n.Pos, "%S %S does not escape", funcSym(loc.curfn), n)
1289 } else if n.Esc == EscHeap {
1290 Warnl(n.Pos, "leaking param: %S", n)
1291 } else {
1292 if n.Esc&EscContentEscapes != 0 {
1293 Warnl(n.Pos, "leaking param content: %S", n)
1294 }
1295 for i := 0; i < numEscReturns; i++ {
1296 if x := getEscReturn(n.Esc, i); x >= 0 {
1297 res := n.Name.Curfn.Type.Results().Field(i).Sym
1298 Warnl(n.Pos, "leaking param: %S to result %v level=%d", n, res, x)
1299 }
1300 }
1301 }
1302 }
1303 } else {
1304 n.Esc = EscNone
1305 if loc.transient {
1306 switch n.Op {
1307 case OCALLPART, OCLOSURE, ODDDARG, OARRAYLIT, OSLICELIT, OPTRLIT, OSTRUCTLIT:
1308 n.SetNoescape(true)
1309 }
1310 }
1311
1312 if Debug['m'] != 0 && n.Op != ONAME && n.Op != OTYPESW && n.Op != ORANGE && n.Op != ODEFER {
1313 Warnl(n.Pos, "%S %S does not escape", funcSym(loc.curfn), n)
1314 }
1315 }
1316 }
1317 }
1318
1319 func (l *EscLocation) isName(c Class) bool {
1320 return l.n != nil && l.n.Op == ONAME && l.n.Class() == c
1321 }
1322
1323 func finalizeEsc(esc uint16) uint16 {
1324 esc = optimizeReturns(esc)
1325
1326 if esc>>EscReturnBits != 0 {
1327 esc |= EscReturn
1328 } else if esc&EscMask == 0 {
1329 esc |= EscNone
1330 }
1331
1332 return esc
1333 }
1334
1335 func optimizeReturns(esc uint16) uint16 {
1336 if esc&EscContentEscapes != 0 {
1337
1338
1339
1340 for i := 0; i < numEscReturns; i++ {
1341 if x := getEscReturn(esc, i); x >= 1 {
1342 esc = setEscReturn(esc, i, -1)
1343 }
1344 }
1345 }
1346 return esc
1347 }
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367 const numEscReturns = (16 - EscReturnBits) / bitsPerOutputInTag
1368
1369 func getEscReturn(esc uint16, i int) int {
1370 return int((esc>>escReturnShift(i))&bitsMaskForTag) - 1
1371 }
1372
1373 func setEscReturn(esc uint16, i, v int) uint16 {
1374 if v < -1 {
1375 Fatalf("invalid esc return value: %v", v)
1376 }
1377 if v > maxEncodedLevel {
1378 v = maxEncodedLevel
1379 }
1380
1381 shift := escReturnShift(i)
1382 esc &^= bitsMaskForTag << shift
1383 esc |= uint16(v+1) << shift
1384 return esc
1385 }
1386
1387 func escReturnShift(i int) uint {
1388 if uint(i) >= numEscReturns {
1389 Fatalf("esc return index out of bounds: %v", i)
1390 }
1391 return uint(EscReturnBits + i*bitsPerOutputInTag)
1392 }
1393
View as plain text