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
41
42 var fns []*ir.Name
43 switch call.Op() {
44 case ir.OCALLFUNC:
45 ro := e.reassignOracle(e.curfn)
46 v := ro.StaticValue(call.Fun)
47 if fn := ir.StaticCalleeName(v); fn != nil {
48 fns = []*ir.Name{fn}
49 } else if name, ok := v.(*ir.Name); ok {
50 fns = resolveAssignedCallees(ro.FuncAssignments(name.Canonical()))
51 }
52 }
53
54 fntype := call.Fun.Type()
55 if len(fns) == 1 {
56 fntype = fns[0].Type()
57 }
58
59
60 if ks != nil {
61 for _, f := range fns {
62 if e.inMutualBatch(f) {
63 for i, result := range f.Type().Results() {
64 e.expr(ks[i], result.Nname.(*ir.Name))
65 }
66 }
67 }
68 }
69
70 var recvArg ir.Node
71 if call.Op() == ir.OCALLFUNC {
72
73 calleeK := e.discardHole()
74 if len(fns) == 0 {
75 for _, k := range ks {
76 if k.dst != &e.blankLoc {
77
78
79
80
81 calleeK = e.calleeHole().note(call, "callee operand")
82 break
83 }
84 }
85 }
86 e.expr(calleeK, call.Fun)
87 } else {
88 recvArg = call.Fun.(*ir.SelectorExpr).X
89 }
90
91 args := call.Args
92 if fntype.Recv() != nil {
93 if recvArg == nil {
94
95
96 recvArg, args = args[0], args[1:]
97 }
98 }
99
100 if call.IsCompilerVarLive {
101
102 if recvArg != nil {
103 argument(e.discardHole(), recvArg)
104 }
105 for _, arg := range args {
106 argument(e.discardHole(), arg)
107 }
108 } else if isEscapeNonString(fns, fntype) {
109
110
111
112
113
114
115
116
117 k := e.heapHole()
118 if !hasNonStringPointers(fntype.Params()[1].Type) {
119 k = e.discardHole()
120 }
121 for _, arg := range args {
122 argument(k, arg)
123 }
124 } else {
125 if recvArg != nil {
126 e.rewriteArgument(recvArg, call, fns)
127 argument(e.mergedTagHole(ks, fns, -1, len(fntype.Params())), recvArg)
128 }
129 for i := range fntype.Params() {
130 e.rewriteArgument(args[i], call, fns)
131 argument(e.mergedTagHole(ks, fns, i, len(fntype.Params())), args[i])
132 }
133 }
134
135 case ir.OINLCALL:
136 call := call.(*ir.InlinedCallExpr)
137 e.stmts(call.Body)
138 for i, result := range call.ReturnVars {
139 k := e.discardHole()
140 if ks != nil {
141 k = ks[i]
142 }
143 e.expr(k, result)
144 }
145
146 case ir.OAPPEND:
147 call := call.(*ir.CallExpr)
148 args := call.Args
149
150
151
152
153
154 appendeeK := e.teeHole(ks[0], e.mutatorHole())
155 if args[0].Type().Elem().HasPointers() {
156 appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice"))
157 }
158 argument(appendeeK, args[0])
159
160 if call.IsDDD {
161 appendedK := e.discardHole()
162 if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() {
163 appendedK = e.heapHole().deref(call, "appended slice...")
164 }
165 argument(appendedK, args[1])
166 } else {
167 for i := 1; i < len(args); i++ {
168 argument(e.heapHole(), args[i])
169 }
170 }
171 e.discard(call.RType)
172
173
174
175
176
177 backingStore := e.spill(ks[0], call)
178
179 backingStore.dst.loopDepth = 0
180
181 case ir.OCOPY:
182 call := call.(*ir.BinaryExpr)
183 argument(e.mutatorHole(), call.X)
184
185 copiedK := e.discardHole()
186 if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() {
187 copiedK = e.heapHole().deref(call, "copied slice")
188 }
189 argument(copiedK, call.Y)
190 e.discard(call.RType)
191
192 case ir.OPANIC:
193 call := call.(*ir.UnaryExpr)
194 argument(e.heapHole(), call.X)
195
196 case ir.OCOMPLEX:
197 call := call.(*ir.BinaryExpr)
198 e.discard(call.X)
199 e.discard(call.Y)
200
201 case ir.ODELETE, ir.OPRINT, ir.OPRINTLN, ir.ORECOVER:
202 call := call.(*ir.CallExpr)
203 for _, arg := range call.Args {
204 e.discard(arg)
205 }
206 e.discard(call.RType)
207
208
209
210 case ir.OMIN, ir.OMAX:
211 call := call.(*ir.CallExpr)
212 for _, arg := range call.Args {
213 argument(ks[0], arg)
214 }
215 e.discard(call.RType)
216
217 case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE:
218 call := call.(*ir.UnaryExpr)
219 e.discard(call.X)
220
221 case ir.OCLEAR:
222 call := call.(*ir.UnaryExpr)
223 argument(e.mutatorHole(), call.X)
224
225 case ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
226 call := call.(*ir.UnaryExpr)
227 argument(ks[0], call.X)
228
229 case ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
230 call := call.(*ir.BinaryExpr)
231 argument(ks[0], call.X)
232 e.discard(call.Y)
233 e.discard(call.RType)
234 }
235 }
236
237
238 func (e *escape) goDeferStmt(n *ir.GoDeferStmt) {
239 k := e.heapHole()
240 if n.Op() == ir.ODEFER && e.loopDepth == 1 && n.DeferAt == nil {
241
242
243 k = e.later(e.discardHole())
244
245
246
247 n.SetEsc(ir.EscNever)
248 }
249
250
251
252
253
254
255
256
257 call, ok := n.Call.(*ir.CallExpr)
258 if !ok || call.Op() != ir.OCALLFUNC {
259 base.FatalfAt(n.Pos(), "expected function call: %v", n.Call)
260 }
261 if sig := call.Fun.Type(); sig.NumParams()+sig.NumResults() != 0 {
262 base.FatalfAt(n.Pos(), "expected signature without parameters or results: %v", sig)
263 }
264
265 if clo, ok := call.Fun.(*ir.ClosureExpr); ok && n.Op() == ir.OGO {
266 clo.IsGoWrap = true
267 }
268
269 e.expr(k, call.Fun)
270 }
271
272
273
274 func (e *escape) rewriteArgument(arg ir.Node, call *ir.CallExpr, fns []*ir.Name) {
275 var pragma ir.PragmaFlag
276 for _, fn := range fns {
277 if fn.Func != nil {
278 pragma |= fn.Func.Pragma
279 }
280 }
281 if pragma&(ir.UintptrKeepAlive|ir.UintptrEscapes) == 0 {
282 return
283 }
284
285
286
287
288 unsafeUintptr := func(arg ir.Node) {
289
290
291
292
293 conv, ok := arg.(*ir.ConvExpr)
294 if !ok || conv.Op() != ir.OCONVNOP {
295 return
296 }
297 if !conv.X.Type().IsUnsafePtr() || !conv.Type().IsUintptr() {
298 return
299 }
300
301
302
303
304
305
306 tmp := e.copyExpr(conv.Pos(), conv.X, call.PtrInit())
307 conv.X = tmp
308
309 k := e.mutatorHole()
310 if pragma&ir.UintptrEscapes != 0 {
311 k = e.heapHole().note(conv, "//go:uintptrescapes")
312 }
313 e.flow(k, e.oldLoc(tmp))
314
315 if pragma&ir.UintptrKeepAlive != 0 {
316 tmp.SetAddrtaken(true)
317 call.KeepAlive = append(call.KeepAlive, tmp)
318 }
319 }
320
321
322
323
324
325
326
327
328
329
330
331 if arg.Op() == ir.OSLICELIT {
332 list := arg.(*ir.CompLitExpr).List
333 for _, el := range list {
334 if el.Op() == ir.OKEY {
335 el = el.(*ir.KeyExpr).Value
336 }
337 unsafeUintptr(el)
338 }
339 } else {
340 unsafeUintptr(arg)
341 }
342 }
343
344
345
346
347 func (e *escape) copyExpr(pos src.XPos, expr ir.Node, init *ir.Nodes) *ir.Name {
348 if ir.HasUniquePos(expr) {
349 pos = expr.Pos()
350 }
351
352 tmp := typecheck.TempAt(pos, e.curfn, expr.Type())
353
354 stmts := []ir.Node{
355 ir.NewDecl(pos, ir.ODCL, tmp),
356 ir.NewAssignStmt(pos, tmp, expr),
357 }
358 typecheck.Stmts(stmts)
359 init.Append(stmts...)
360
361 e.newLoc(tmp, true)
362 e.stmts(stmts)
363
364 return tmp
365 }
366
367 func (e *escape) mergedTagHole(ks []hole, fns []*ir.Name, paramIdx int, nParams int) hole {
368 if len(fns) == 0 {
369 return e.heapHole()
370 }
371 holes := make([]hole, 0, len(fns))
372 for _, f := range fns {
373 offset := nParams - len(f.Type().Params())
374 j := paramIdx - offset
375 var p *types.Field
376 if j >= 0 {
377 p = f.Type().Params()[j]
378 } else {
379 p = f.Type().Recv()
380 }
381 holes = append(holes, e.tagHole(ks, f, p))
382 }
383 return e.teeHole(holes...)
384 }
385
386
387
388
389
390 func (e *escape) tagHole(ks []hole, fn *ir.Name, param *types.Field) hole {
391
392 if fn == nil {
393 return e.heapHole()
394 }
395
396 if e.inMutualBatch(fn) {
397 if param.Nname == nil {
398 return e.discardHole()
399 }
400 return e.addr(param.Nname.(*ir.Name))
401 }
402
403
404
405 var tagKs []hole
406 esc := parseLeaks(param.Note)
407
408 if x := esc.Heap(); x >= 0 {
409 tagKs = append(tagKs, e.heapHole().shift(x))
410 }
411 if x := esc.Mutator(); x >= 0 {
412 tagKs = append(tagKs, e.mutatorHole().shift(x))
413 }
414 if x := esc.Callee(); x >= 0 {
415 tagKs = append(tagKs, e.calleeHole().shift(x))
416 }
417
418 if ks != nil {
419 for i := 0; i < numEscResults; i++ {
420 if x := esc.Result(i); x >= 0 {
421 tagKs = append(tagKs, ks[i].shift(x))
422 }
423 }
424 }
425
426 return e.teeHole(tagKs...)
427 }
428
429
430
431
432 func resolveAssignedCallees(assigns []*ir.AssignStmt) []*ir.Name {
433 fns := make([]*ir.Name, 0, len(assigns))
434 for _, as := range assigns {
435 if ir.IsZero(as.Y) {
436 continue
437 }
438 callee := ir.StaticCalleeName(as.Y)
439 if callee == nil {
440 return nil
441 }
442 if callee.Func != nil && callee.Func.Pragma&(ir.UintptrKeepAlive|ir.UintptrEscapes) != 0 {
443 return nil
444 }
445 fns = append(fns, callee)
446 }
447 return fns
448 }
449
450 func isEscapeNonString(fns []*ir.Name, fntype *types.Type) bool {
451 return len(fns) == 1 &&
452 fns[0].Sym().Pkg.Path == "internal/abi" &&
453 strings.HasPrefix(fns[0].Sym().Name, "EscapeNonString[") &&
454 len(fntype.Params()) == 2 && fntype.Params()[1].Type.IsShape()
455 }
456
457 func hasNonStringPointers(t *types.Type) bool {
458 if !t.HasPointers() {
459 return false
460 }
461 switch t.Kind() {
462 case types.TSTRING:
463 return false
464 case types.TSTRUCT:
465 for _, f := range t.Fields() {
466 if hasNonStringPointers(f.Type) {
467 return true
468 }
469 }
470 return false
471 case types.TARRAY:
472 return hasNonStringPointers(t.Elem())
473 }
474 return true
475 }
476
View as plain text