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