]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/escape/utils.go
[dev.typeparams] cmd/compile: separate out creating instantiations from creating...
[gostls13.git] / src / cmd / compile / internal / escape / utils.go
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.
4
5 package escape
6
7 import (
8         "cmd/compile/internal/ir"
9         "cmd/compile/internal/typecheck"
10 )
11
12 func isSliceSelfAssign(dst, src ir.Node) bool {
13         // Detect the following special case.
14         //
15         //      func (b *Buffer) Foo() {
16         //              n, m := ...
17         //              b.buf = b.buf[n:m]
18         //      }
19         //
20         // This assignment is a no-op for escape analysis,
21         // it does not store any new pointers into b that were not already there.
22         // However, without this special case b will escape, because we assign to OIND/ODOTPTR.
23         // Here we assume that the statement will not contain calls,
24         // that is, that order will move any calls to init.
25         // Otherwise base ONAME value could change between the moments
26         // when we evaluate it for dst and for src.
27
28         // dst is ONAME dereference.
29         var dstX ir.Node
30         switch dst.Op() {
31         default:
32                 return false
33         case ir.ODEREF:
34                 dst := dst.(*ir.StarExpr)
35                 dstX = dst.X
36         case ir.ODOTPTR:
37                 dst := dst.(*ir.SelectorExpr)
38                 dstX = dst.X
39         }
40         if dstX.Op() != ir.ONAME {
41                 return false
42         }
43         // src is a slice operation.
44         switch src.Op() {
45         case ir.OSLICE, ir.OSLICE3, ir.OSLICESTR:
46                 // OK.
47         case ir.OSLICEARR, ir.OSLICE3ARR:
48                 // Since arrays are embedded into containing object,
49                 // slice of non-pointer array will introduce a new pointer into b that was not already there
50                 // (pointer to b itself). After such assignment, if b contents escape,
51                 // b escapes as well. If we ignore such OSLICEARR, we will conclude
52                 // that b does not escape when b contents do.
53                 //
54                 // Pointer to an array is OK since it's not stored inside b directly.
55                 // For slicing an array (not pointer to array), there is an implicit OADDR.
56                 // We check that to determine non-pointer array slicing.
57                 src := src.(*ir.SliceExpr)
58                 if src.X.Op() == ir.OADDR {
59                         return false
60                 }
61         default:
62                 return false
63         }
64         // slice is applied to ONAME dereference.
65         var baseX ir.Node
66         switch base := src.(*ir.SliceExpr).X; base.Op() {
67         default:
68                 return false
69         case ir.ODEREF:
70                 base := base.(*ir.StarExpr)
71                 baseX = base.X
72         case ir.ODOTPTR:
73                 base := base.(*ir.SelectorExpr)
74                 baseX = base.X
75         }
76         if baseX.Op() != ir.ONAME {
77                 return false
78         }
79         // dst and src reference the same base ONAME.
80         return dstX.(*ir.Name) == baseX.(*ir.Name)
81 }
82
83 // isSelfAssign reports whether assignment from src to dst can
84 // be ignored by the escape analysis as it's effectively a self-assignment.
85 func isSelfAssign(dst, src ir.Node) bool {
86         if isSliceSelfAssign(dst, src) {
87                 return true
88         }
89
90         // Detect trivial assignments that assign back to the same object.
91         //
92         // It covers these cases:
93         //      val.x = val.y
94         //      val.x[i] = val.y[j]
95         //      val.x1.x2 = val.x1.y2
96         //      ... etc
97         //
98         // These assignments do not change assigned object lifetime.
99
100         if dst == nil || src == nil || dst.Op() != src.Op() {
101                 return false
102         }
103
104         // The expression prefix must be both "safe" and identical.
105         switch dst.Op() {
106         case ir.ODOT, ir.ODOTPTR:
107                 // Safe trailing accessors that are permitted to differ.
108                 dst := dst.(*ir.SelectorExpr)
109                 src := src.(*ir.SelectorExpr)
110                 return ir.SameSafeExpr(dst.X, src.X)
111         case ir.OINDEX:
112                 dst := dst.(*ir.IndexExpr)
113                 src := src.(*ir.IndexExpr)
114                 if mayAffectMemory(dst.Index) || mayAffectMemory(src.Index) {
115                         return false
116                 }
117                 return ir.SameSafeExpr(dst.X, src.X)
118         default:
119                 return false
120         }
121 }
122
123 // mayAffectMemory reports whether evaluation of n may affect the program's
124 // memory state. If the expression can't affect memory state, then it can be
125 // safely ignored by the escape analysis.
126 func mayAffectMemory(n ir.Node) bool {
127         // We may want to use a list of "memory safe" ops instead of generally
128         // "side-effect free", which would include all calls and other ops that can
129         // allocate or change global state. For now, it's safer to start with the latter.
130         //
131         // We're ignoring things like division by zero, index out of range,
132         // and nil pointer dereference here.
133
134         // TODO(rsc): It seems like it should be possible to replace this with
135         // an ir.Any looking for any op that's not the ones in the case statement.
136         // But that produces changes in the compiled output detected by buildall.
137         switch n.Op() {
138         case ir.ONAME, ir.OLITERAL, ir.ONIL:
139                 return false
140
141         case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD:
142                 n := n.(*ir.BinaryExpr)
143                 return mayAffectMemory(n.X) || mayAffectMemory(n.Y)
144
145         case ir.OINDEX:
146                 n := n.(*ir.IndexExpr)
147                 return mayAffectMemory(n.X) || mayAffectMemory(n.Index)
148
149         case ir.OCONVNOP, ir.OCONV:
150                 n := n.(*ir.ConvExpr)
151                 return mayAffectMemory(n.X)
152
153         case ir.OLEN, ir.OCAP, ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
154                 n := n.(*ir.UnaryExpr)
155                 return mayAffectMemory(n.X)
156
157         case ir.ODOT, ir.ODOTPTR:
158                 n := n.(*ir.SelectorExpr)
159                 return mayAffectMemory(n.X)
160
161         case ir.ODEREF:
162                 n := n.(*ir.StarExpr)
163                 return mayAffectMemory(n.X)
164
165         default:
166                 return true
167         }
168 }
169
170 // HeapAllocReason returns the reason the given Node must be heap
171 // allocated, or the empty string if it doesn't.
172 func HeapAllocReason(n ir.Node) string {
173         if n == nil || n.Type() == nil {
174                 return ""
175         }
176
177         // Parameters are always passed via the stack.
178         if n.Op() == ir.ONAME {
179                 n := n.(*ir.Name)
180                 if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
181                         return ""
182                 }
183         }
184
185         if n.Type().Width > ir.MaxStackVarSize {
186                 return "too large for stack"
187         }
188
189         if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Width >= ir.MaxImplicitStackVarSize {
190                 return "too large for stack"
191         }
192
193         if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() >= ir.MaxImplicitStackVarSize {
194                 return "too large for stack"
195         }
196         if n.Op() == ir.OMETHVALUE && typecheck.PartialCallType(n.(*ir.SelectorExpr)).Size() >= ir.MaxImplicitStackVarSize {
197                 return "too large for stack"
198         }
199
200         if n.Op() == ir.OMAKESLICE {
201                 n := n.(*ir.MakeExpr)
202                 r := n.Cap
203                 if r == nil {
204                         r = n.Len
205                 }
206                 if !ir.IsSmallIntConst(r) {
207                         return "non-constant size"
208                 }
209                 if t := n.Type(); t.Elem().Width != 0 && ir.Int64Val(r) >= ir.MaxImplicitStackVarSize/t.Elem().Width {
210                         return "too large for stack"
211                 }
212         }
213
214         return ""
215 }