1
2
3
4
5 package escape
6
7 import (
8 "cmd/compile/internal/base"
9 "cmd/compile/internal/ir"
10 "cmd/compile/internal/typecheck"
11 "cmd/compile/internal/types"
12 "cmd/internal/src"
13 "strings"
14 )
15
16
17
18
19 func (e *escape) call(ks []hole, call ir.Node) {
20 argument := func(k hole, arg ir.Node) {
21
22 e.expr(k.note(call, "call parameter"), arg)
23 }
24
25 switch call.Op() {
26 default:
27 ir.Dump("esc", call)
28 base.Fatalf("unexpected call op: %v", call.Op())
29
30 case ir.OCALLFUNC, ir.OCALLINTER:
31 call := call.(*ir.CallExpr)
32 typecheck.AssertFixedCall(call)
33
34
35
36
37
38
39
40 var fn *ir.Name
41 switch call.Op() {
42 case ir.OCALLFUNC:
43 v := ir.StaticValue(call.Fun)
44 fn = ir.StaticCalleeName(v)
45 }
46
47 fntype := call.Fun.Type()
48 if fn != nil {
49 fntype = fn.Type()
50 }
51
52 if ks != nil && fn != nil && e.inMutualBatch(fn) {
53 for i, result := range fn.Type().Results() {
54 e.expr(ks[i], result.Nname.(*ir.Name))
55 }
56 }
57
58 var recvArg ir.Node
59 if call.Op() == ir.OCALLFUNC {
60
61 calleeK := e.discardHole()
62 if fn == nil {
63 for _, k := range ks {
64 if k.dst != &e.blankLoc {
65
66
67
68
69 calleeK = e.calleeHole().note(call, "callee operand")
70 break
71 }
72 }
73 }
74 e.expr(calleeK, call.Fun)
75 } else {
76 recvArg = call.Fun.(*ir.SelectorExpr).X
77 }
78
79
80
81 argumentParam := func(param *types.Field, arg ir.Node) {
82 e.rewriteArgument(arg, call, fn)
83 argument(e.tagHole(ks, fn, param), arg)
84 }
85
86
87
88
89
90
91
92
93
94 if fn != nil && fn.Sym().Pkg.Path == "hash/maphash" && strings.HasPrefix(fn.Sym().Name, "escapeForHash[") {
95 ps := fntype.Params()
96 if len(ps) == 2 && ps[1].Type.IsShape() {
97 if !hasNonStringPointers(ps[1].Type) {
98 argumentParam = func(param *types.Field, arg ir.Node) {
99 argument(e.discardHole(), arg)
100 }
101 } else {
102 argumentParam = func(param *types.Field, arg ir.Node) {
103 argument(e.heapHole(), arg)
104 }
105 }
106 }
107 }
108
109 args := call.Args
110 if recvParam := fntype.Recv(); recvParam != nil {
111 if recvArg == nil {
112
113
114 recvArg, args = args[0], args[1:]
115 }
116
117 argumentParam(recvParam, recvArg)
118 }
119
120 for i, param := range fntype.Params() {
121 argumentParam(param, args[i])
122 }
123
124 case ir.OINLCALL:
125 call := call.(*ir.InlinedCallExpr)
126 e.stmts(call.Body)
127 for i, result := range call.ReturnVars {
128 k := e.discardHole()
129 if ks != nil {
130 k = ks[i]
131 }
132 e.expr(k, result)
133 }
134
135 case ir.OAPPEND:
136 call := call.(*ir.CallExpr)
137 args := call.Args
138
139
140
141
142
143 appendeeK := e.teeHole(ks[0], e.mutatorHole())
144 if args[0].Type().Elem().HasPointers() {
145 appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice"))
146 }
147 argument(appendeeK, args[0])
148
149 if call.IsDDD {
150 appendedK := e.discardHole()
151 if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() {
152 appendedK = e.heapHole().deref(call, "appended slice...")
153 }
154 argument(appendedK, args[1])
155 } else {
156 for i := 1; i < len(args); i++ {
157 argument(e.heapHole(), args[i])
158 }
159 }
160 e.discard(call.RType)
161
162 case ir.OCOPY:
163 call := call.(*ir.BinaryExpr)
164 argument(e.mutatorHole(), call.X)
165
166 copiedK := e.discardHole()
167 if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() {
168 copiedK = e.heapHole().deref(call, "copied slice")
169 }
170 argument(copiedK, call.Y)
171 e.discard(call.RType)
172
173 case ir.OPANIC:
174 call := call.(*ir.UnaryExpr)
175 argument(e.heapHole(), call.X)
176
177 case ir.OCOMPLEX:
178 call := call.(*ir.BinaryExpr)
179 e.discard(call.X)
180 e.discard(call.Y)
181
182 case ir.ODELETE, ir.OPRINT, ir.OPRINTLN, ir.ORECOVERFP:
183 call := call.(*ir.CallExpr)
184 for _, arg := range call.Args {
185 e.discard(arg)
186 }
187 e.discard(call.RType)
188
189 case ir.OMIN, ir.OMAX:
190 call := call.(*ir.CallExpr)
191 for _, arg := range call.Args {
192 argument(ks[0], arg)
193 }
194 e.discard(call.RType)
195
196 case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE:
197 call := call.(*ir.UnaryExpr)
198 e.discard(call.X)
199
200 case ir.OCLEAR:
201 call := call.(*ir.UnaryExpr)
202 argument(e.mutatorHole(), call.X)
203
204 case ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
205 call := call.(*ir.UnaryExpr)
206 argument(ks[0], call.X)
207
208 case ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
209 call := call.(*ir.BinaryExpr)
210 argument(ks[0], call.X)
211 e.discard(call.Y)
212 e.discard(call.RType)
213 }
214 }
215
216
217 func (e *escape) goDeferStmt(n *ir.GoDeferStmt) {
218 k := e.heapHole()
219 if n.Op() == ir.ODEFER && e.loopDepth == 1 && n.DeferAt == nil {
220
221
222 k = e.later(e.discardHole())
223
224
225
226 n.SetEsc(ir.EscNever)
227 }
228
229
230
231
232
233
234
235
236 call, ok := n.Call.(*ir.CallExpr)
237 if !ok || call.Op() != ir.OCALLFUNC {
238 base.FatalfAt(n.Pos(), "expected function call: %v", n.Call)
239 }
240 if sig := call.Fun.Type(); sig.NumParams()+sig.NumResults() != 0 {
241 base.FatalfAt(n.Pos(), "expected signature without parameters or results: %v", sig)
242 }
243
244 if clo, ok := call.Fun.(*ir.ClosureExpr); ok && n.Op() == ir.OGO {
245 clo.IsGoWrap = true
246 }
247
248 e.expr(k, call.Fun)
249 }
250
251
252
253 func (e *escape) rewriteArgument(arg ir.Node, call *ir.CallExpr, fn *ir.Name) {
254 if fn == nil || fn.Func == nil {
255 return
256 }
257 pragma := fn.Func.Pragma
258 if pragma&(ir.UintptrKeepAlive|ir.UintptrEscapes) == 0 {
259 return
260 }
261
262
263
264
265 unsafeUintptr := func(arg ir.Node) {
266
267
268
269
270 conv, ok := arg.(*ir.ConvExpr)
271 if !ok || conv.Op() != ir.OCONVNOP {
272 return
273 }
274 if !conv.X.Type().IsUnsafePtr() || !conv.Type().IsUintptr() {
275 return
276 }
277
278
279
280
281
282
283 tmp := e.copyExpr(conv.Pos(), conv.X, call.PtrInit())
284 conv.X = tmp
285
286 k := e.mutatorHole()
287 if pragma&ir.UintptrEscapes != 0 {
288 k = e.heapHole().note(conv, "//go:uintptrescapes")
289 }
290 e.flow(k, e.oldLoc(tmp))
291
292 if pragma&ir.UintptrKeepAlive != 0 {
293 tmp.SetAddrtaken(true)
294 call.KeepAlive = append(call.KeepAlive, tmp)
295 }
296 }
297
298
299
300
301
302
303
304
305
306
307
308 if arg.Op() == ir.OSLICELIT {
309 list := arg.(*ir.CompLitExpr).List
310 for _, el := range list {
311 if el.Op() == ir.OKEY {
312 el = el.(*ir.KeyExpr).Value
313 }
314 unsafeUintptr(el)
315 }
316 } else {
317 unsafeUintptr(arg)
318 }
319 }
320
321
322
323
324 func (e *escape) copyExpr(pos src.XPos, expr ir.Node, init *ir.Nodes) *ir.Name {
325 if ir.HasUniquePos(expr) {
326 pos = expr.Pos()
327 }
328
329 tmp := typecheck.TempAt(pos, e.curfn, expr.Type())
330
331 stmts := []ir.Node{
332 ir.NewDecl(pos, ir.ODCL, tmp),
333 ir.NewAssignStmt(pos, tmp, expr),
334 }
335 typecheck.Stmts(stmts)
336 init.Append(stmts...)
337
338 e.newLoc(tmp, true)
339 e.stmts(stmts)
340
341 return tmp
342 }
343
344
345
346
347
348 func (e *escape) tagHole(ks []hole, fn *ir.Name, param *types.Field) hole {
349
350 if fn == nil {
351 return e.heapHole()
352 }
353
354 if e.inMutualBatch(fn) {
355 if param.Nname == nil {
356 return e.discardHole()
357 }
358 return e.addr(param.Nname.(*ir.Name))
359 }
360
361
362
363 var tagKs []hole
364 esc := parseLeaks(param.Note)
365
366 if x := esc.Heap(); x >= 0 {
367 tagKs = append(tagKs, e.heapHole().shift(x))
368 }
369 if x := esc.Mutator(); x >= 0 {
370 tagKs = append(tagKs, e.mutatorHole().shift(x))
371 }
372 if x := esc.Callee(); x >= 0 {
373 tagKs = append(tagKs, e.calleeHole().shift(x))
374 }
375
376 if ks != nil {
377 for i := 0; i < numEscResults; i++ {
378 if x := esc.Result(i); x >= 0 {
379 tagKs = append(tagKs, ks[i].shift(x))
380 }
381 }
382 }
383
384 return e.teeHole(tagKs...)
385 }
386
387 func hasNonStringPointers(t *types.Type) bool {
388 if !t.HasPointers() {
389 return false
390 }
391 switch t.Kind() {
392 case types.TSTRING:
393 return false
394 case types.TSTRUCT:
395 for _, f := range t.Fields() {
396 if hasNonStringPointers(f.Type) {
397 return true
398 }
399 }
400 return false
401 case types.TARRAY:
402 return hasNonStringPointers(t.Elem())
403 }
404 return true
405 }
406
View as plain text