github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/escape/call.go (about) 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 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 9 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 10 "github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck" 11 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 12 "github.com/bir3/gocompiler/src/cmd/internal/src" 13 ) 14 15 // call evaluates a call expressions, including builtin calls. ks 16 // should contain the holes representing where the function callee's 17 // results flows. 18 func (e *escape) call(ks []hole, call ir.Node) { 19 var init ir.Nodes 20 e.callCommon(ks, call, &init, nil) 21 if len(init) != 0 { 22 call.(ir.InitNode).PtrInit().Append(init...) 23 } 24 } 25 26 func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir.Func) { 27 28 // argumentPragma handles escape analysis of argument *argp to the 29 // given hole. If the function callee is known, pragma is the 30 // function's pragma flags; otherwise 0. 31 argumentFunc := func(fn *ir.Name, k hole, argp *ir.Node) { 32 e.rewriteArgument(argp, init, call, fn, wrapper) 33 34 e.expr(k.note(call, "call parameter"), *argp) 35 } 36 37 argument := func(k hole, argp *ir.Node) { 38 argumentFunc(nil, k, argp) 39 } 40 41 argumentRType := func(rtypep *ir.Node) { 42 rtype := *rtypep 43 if rtype == nil { 44 return 45 } 46 // common case: static rtype/itab argument, which can be evaluated within the wrapper instead. 47 if addr, ok := rtype.(*ir.AddrExpr); ok && addr.Op() == ir.OADDR && addr.X.Op() == ir.OLINKSYMOFFSET { 48 return 49 } 50 e.wrapExpr(rtype.Pos(), rtypep, init, call, wrapper) 51 } 52 53 switch call.Op() { 54 default: 55 ir.Dump("esc", call) 56 base.Fatalf("unexpected call op: %v", call.Op()) 57 58 case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER: 59 call := call.(*ir.CallExpr) 60 typecheck.FixVariadicCall(call) 61 typecheck.FixMethodCall(call) 62 63 // Pick out the function callee, if statically known. 64 // 65 // TODO(mdempsky): Change fn from *ir.Name to *ir.Func, but some 66 // functions (e.g., runtime builtins, method wrappers, generated 67 // eq/hash functions) don't have it set. Investigate whether 68 // that's a concern. 69 var fn *ir.Name 70 switch call.Op() { 71 case ir.OCALLFUNC: 72 // If we have a direct call to a closure (not just one we were 73 // able to statically resolve with ir.StaticValue), mark it as 74 // such so batch.outlives can optimize the flow results. 75 if call.X.Op() == ir.OCLOSURE { 76 call.X.(*ir.ClosureExpr).Func.SetClosureCalled(true) 77 } 78 79 switch v := ir.StaticValue(call.X); v.Op() { 80 case ir.ONAME: 81 if v := v.(*ir.Name); v.Class == ir.PFUNC { 82 fn = v 83 } 84 case ir.OCLOSURE: 85 fn = v.(*ir.ClosureExpr).Func.Nname 86 case ir.OMETHEXPR: 87 fn = ir.MethodExprName(v) 88 } 89 case ir.OCALLMETH: 90 base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck") 91 } 92 93 fntype := call.X.Type() 94 if fn != nil { 95 fntype = fn.Type() 96 } 97 98 if ks != nil && fn != nil && e.inMutualBatch(fn) { 99 for i, result := range fn.Type().Results().FieldSlice() { 100 e.expr(ks[i], ir.AsNode(result.Nname)) 101 } 102 } 103 104 var recvp *ir.Node 105 if call.Op() == ir.OCALLFUNC { 106 // Evaluate callee function expression. 107 // 108 // Note: We use argument and not argumentFunc, because while 109 // call.X here may be an argument to runtime.{new,defer}proc, 110 // it's not an argument to fn itself. 111 argument(e.discardHole(), &call.X) 112 } else { 113 recvp = &call.X.(*ir.SelectorExpr).X 114 } 115 116 args := call.Args 117 if recv := fntype.Recv(); recv != nil { 118 if recvp == nil { 119 // Function call using method expression. Recevier argument is 120 // at the front of the regular arguments list. 121 recvp = &args[0] 122 args = args[1:] 123 } 124 125 argumentFunc(fn, e.tagHole(ks, fn, recv), recvp) 126 } 127 128 for i, param := range fntype.Params().FieldSlice() { 129 argumentFunc(fn, e.tagHole(ks, fn, param), &args[i]) 130 } 131 132 case ir.OINLCALL: 133 call := call.(*ir.InlinedCallExpr) 134 e.stmts(call.Body) 135 for i, result := range call.ReturnVars { 136 k := e.discardHole() 137 if ks != nil { 138 k = ks[i] 139 } 140 e.expr(k, result) 141 } 142 143 case ir.OAPPEND: 144 call := call.(*ir.CallExpr) 145 args := call.Args 146 147 // Appendee slice may flow directly to the result, if 148 // it has enough capacity. Alternatively, a new heap 149 // slice might be allocated, and all slice elements 150 // might flow to heap. 151 appendeeK := ks[0] 152 if args[0].Type().Elem().HasPointers() { 153 appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice")) 154 } 155 argument(appendeeK, &args[0]) 156 157 if call.IsDDD { 158 appendedK := e.discardHole() 159 if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() { 160 appendedK = e.heapHole().deref(call, "appended slice...") 161 } 162 argument(appendedK, &args[1]) 163 } else { 164 for i := 1; i < len(args); i++ { 165 argument(e.heapHole(), &args[i]) 166 } 167 } 168 argumentRType(&call.RType) 169 170 case ir.OCOPY: 171 call := call.(*ir.BinaryExpr) 172 argument(e.discardHole(), &call.X) 173 174 copiedK := e.discardHole() 175 if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() { 176 copiedK = e.heapHole().deref(call, "copied slice") 177 } 178 argument(copiedK, &call.Y) 179 argumentRType(&call.RType) 180 181 case ir.OPANIC: 182 call := call.(*ir.UnaryExpr) 183 argument(e.heapHole(), &call.X) 184 185 case ir.OCOMPLEX: 186 call := call.(*ir.BinaryExpr) 187 argument(e.discardHole(), &call.X) 188 argument(e.discardHole(), &call.Y) 189 190 case ir.ODELETE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: 191 call := call.(*ir.CallExpr) 192 fixRecoverCall(call) 193 for i := range call.Args { 194 argument(e.discardHole(), &call.Args[i]) 195 } 196 argumentRType(&call.RType) 197 198 case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE: 199 call := call.(*ir.UnaryExpr) 200 argument(e.discardHole(), &call.X) 201 202 case ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA: 203 call := call.(*ir.UnaryExpr) 204 argument(ks[0], &call.X) 205 206 case ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING: 207 call := call.(*ir.BinaryExpr) 208 argument(ks[0], &call.X) 209 argument(e.discardHole(), &call.Y) 210 argumentRType(&call.RType) 211 } 212 } 213 214 // goDeferStmt analyzes a "go" or "defer" statement. 215 // 216 // In the process, it also normalizes the statement to always use a 217 // simple function call with no arguments and no results. For example, 218 // it rewrites: 219 // 220 // defer f(x, y) 221 // 222 // into: 223 // 224 // x1, y1 := x, y 225 // defer func() { f(x1, y1) }() 226 func (e *escape) goDeferStmt(n *ir.GoDeferStmt) { 227 k := e.heapHole() 228 if n.Op() == ir.ODEFER && e.loopDepth == 1 { 229 // Top-level defer arguments don't escape to the heap, 230 // but they do need to last until they're invoked. 231 k = e.later(e.discardHole()) 232 233 // force stack allocation of defer record, unless 234 // open-coded defers are used (see ssa.go) 235 n.SetEsc(ir.EscNever) 236 } 237 238 call := n.Call 239 240 init := n.PtrInit() 241 init.Append(ir.TakeInit(call)...) 242 e.stmts(*init) 243 244 // If the function is already a zero argument/result function call, 245 // just escape analyze it normally. 246 // 247 // Note that the runtime is aware of this optimization for 248 // "go" statements that start in reflect.makeFuncStub or 249 // reflect.methodValueCall. 250 if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC { 251 if sig := call.X.Type(); sig.NumParams()+sig.NumResults() == 0 { 252 if clo, ok := call.X.(*ir.ClosureExpr); ok && n.Op() == ir.OGO { 253 clo.IsGoWrap = true 254 } 255 e.expr(k, call.X) 256 return 257 } 258 } 259 260 // Create a new no-argument function that we'll hand off to defer. 261 fn := ir.NewClosureFunc(n.Pos(), true) 262 fn.SetWrapper(true) 263 fn.Nname.SetType(types.NewSignature(types.LocalPkg, nil, nil, nil, nil)) 264 fn.Body = []ir.Node{call} 265 if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC { 266 // If the callee is a named function, link to the original callee. 267 x := call.X 268 if x.Op() == ir.ONAME && x.(*ir.Name).Class == ir.PFUNC { 269 fn.WrappedFunc = call.X.(*ir.Name).Func 270 } else if x.Op() == ir.OMETHEXPR && ir.MethodExprFunc(x).Nname != nil { 271 fn.WrappedFunc = ir.MethodExprName(x).Func 272 } 273 } 274 275 clo := fn.OClosure 276 if n.Op() == ir.OGO { 277 clo.IsGoWrap = true 278 } 279 280 e.callCommon(nil, call, init, fn) 281 e.closures = append(e.closures, closure{e.spill(k, clo), clo}) 282 283 // Create new top level call to closure. 284 n.Call = ir.NewCallExpr(call.Pos(), ir.OCALL, clo, nil) 285 ir.WithFunc(e.curfn, func() { 286 typecheck.Stmt(n.Call) 287 }) 288 } 289 290 // rewriteArgument rewrites the argument *argp of the given call expression. 291 // fn is the static callee function, if known. 292 // wrapper is the go/defer wrapper function for call, if any. 293 func (e *escape) rewriteArgument(argp *ir.Node, init *ir.Nodes, call ir.Node, fn *ir.Name, wrapper *ir.Func) { 294 var pragma ir.PragmaFlag 295 if fn != nil && fn.Func != nil { 296 pragma = fn.Func.Pragma 297 } 298 299 // unsafeUintptr rewrites "uintptr(ptr)" arguments to syscall-like 300 // functions, so that ptr is kept alive and/or escaped as 301 // appropriate. unsafeUintptr also reports whether it modified arg0. 302 unsafeUintptr := func(arg0 ir.Node) bool { 303 if pragma&(ir.UintptrKeepAlive|ir.UintptrEscapes) == 0 { 304 return false 305 } 306 307 // If the argument is really a pointer being converted to uintptr, 308 // arrange for the pointer to be kept alive until the call returns, 309 // by copying it into a temp and marking that temp 310 // still alive when we pop the temp stack. 311 if arg0.Op() != ir.OCONVNOP || !arg0.Type().IsUintptr() { 312 return false 313 } 314 arg := arg0.(*ir.ConvExpr) 315 316 if !arg.X.Type().IsUnsafePtr() { 317 return false 318 } 319 320 // Create and declare a new pointer-typed temp variable. 321 tmp := e.wrapExpr(arg.Pos(), &arg.X, init, call, wrapper) 322 323 if pragma&ir.UintptrEscapes != 0 { 324 e.flow(e.heapHole().note(arg, "//go:uintptrescapes"), e.oldLoc(tmp)) 325 } 326 327 if pragma&ir.UintptrKeepAlive != 0 { 328 call := call.(*ir.CallExpr) 329 330 // SSA implements CallExpr.KeepAlive using OpVarLive, which 331 // doesn't support PAUTOHEAP variables. I tried changing it to 332 // use OpKeepAlive, but that ran into issues of its own. 333 // For now, the easy solution is to explicitly copy to (yet 334 // another) new temporary variable. 335 keep := tmp 336 if keep.Class == ir.PAUTOHEAP { 337 keep = e.copyExpr(arg.Pos(), tmp, call.PtrInit(), wrapper, false) 338 } 339 340 keep.SetAddrtaken(true) // ensure SSA keeps the tmp variable 341 call.KeepAlive = append(call.KeepAlive, keep) 342 } 343 344 return true 345 } 346 347 visit := func(pos src.XPos, argp *ir.Node) { 348 // Optimize a few common constant expressions. By leaving these 349 // untouched in the call expression, we let the wrapper handle 350 // evaluating them, rather than taking up closure context space. 351 switch arg := *argp; arg.Op() { 352 case ir.OLITERAL, ir.ONIL, ir.OMETHEXPR: 353 return 354 case ir.ONAME: 355 if arg.(*ir.Name).Class == ir.PFUNC { 356 return 357 } 358 } 359 360 if unsafeUintptr(*argp) { 361 return 362 } 363 364 if wrapper != nil { 365 e.wrapExpr(pos, argp, init, call, wrapper) 366 } 367 } 368 369 // Peel away any slice literals for better escape analyze 370 // them. For example: 371 // 372 // go F([]int{a, b}) 373 // 374 // If F doesn't escape its arguments, then the slice can 375 // be allocated on the new goroutine's stack. 376 // 377 // For variadic functions, the compiler has already rewritten: 378 // 379 // f(a, b, c) 380 // 381 // to: 382 // 383 // f([]T{a, b, c}...) 384 // 385 // So we need to look into slice elements to handle uintptr(ptr) 386 // arguments to syscall-like functions correctly. 387 if arg := *argp; arg.Op() == ir.OSLICELIT { 388 list := arg.(*ir.CompLitExpr).List 389 for i := range list { 390 el := &list[i] 391 if list[i].Op() == ir.OKEY { 392 el = &list[i].(*ir.KeyExpr).Value 393 } 394 visit(arg.Pos(), el) 395 } 396 } else { 397 visit(call.Pos(), argp) 398 } 399 } 400 401 // wrapExpr replaces *exprp with a temporary variable copy. If wrapper 402 // is non-nil, the variable will be captured for use within that 403 // function. 404 func (e *escape) wrapExpr(pos src.XPos, exprp *ir.Node, init *ir.Nodes, call ir.Node, wrapper *ir.Func) *ir.Name { 405 tmp := e.copyExpr(pos, *exprp, init, e.curfn, true) 406 407 if wrapper != nil { 408 // Currently for "defer i.M()" if i is nil it panics at the point 409 // of defer statement, not when deferred function is called. We 410 // need to do the nil check outside of the wrapper. 411 if call.Op() == ir.OCALLINTER && exprp == &call.(*ir.CallExpr).X.(*ir.SelectorExpr).X { 412 check := ir.NewUnaryExpr(pos, ir.OCHECKNIL, ir.NewUnaryExpr(pos, ir.OITAB, tmp)) 413 init.Append(typecheck.Stmt(check)) 414 } 415 416 e.oldLoc(tmp).captured = true 417 418 tmp = ir.NewClosureVar(pos, wrapper, tmp) 419 } 420 421 *exprp = tmp 422 return tmp 423 } 424 425 // copyExpr creates and returns a new temporary variable within fn; 426 // appends statements to init to declare and initialize it to expr; 427 // and escape analyzes the data flow if analyze is true. 428 func (e *escape) copyExpr(pos src.XPos, expr ir.Node, init *ir.Nodes, fn *ir.Func, analyze bool) *ir.Name { 429 if ir.HasUniquePos(expr) { 430 pos = expr.Pos() 431 } 432 433 tmp := typecheck.TempAt(pos, fn, expr.Type()) 434 435 stmts := []ir.Node{ 436 ir.NewDecl(pos, ir.ODCL, tmp), 437 ir.NewAssignStmt(pos, tmp, expr), 438 } 439 typecheck.Stmts(stmts) 440 init.Append(stmts...) 441 442 if analyze { 443 e.newLoc(tmp, false) 444 e.stmts(stmts) 445 } 446 447 return tmp 448 } 449 450 // tagHole returns a hole for evaluating an argument passed to param. 451 // ks should contain the holes representing where the function 452 // callee's results flows. fn is the statically-known callee function, 453 // if any. 454 func (e *escape) tagHole(ks []hole, fn *ir.Name, param *types.Field) hole { 455 // If this is a dynamic call, we can't rely on param.Note. 456 if fn == nil { 457 return e.heapHole() 458 } 459 460 if e.inMutualBatch(fn) { 461 return e.addr(ir.AsNode(param.Nname)) 462 } 463 464 // Call to previously tagged function. 465 466 var tagKs []hole 467 468 esc := parseLeaks(param.Note) 469 if x := esc.Heap(); x >= 0 { 470 tagKs = append(tagKs, e.heapHole().shift(x)) 471 } 472 473 if ks != nil { 474 for i := 0; i < numEscResults; i++ { 475 if x := esc.Result(i); x >= 0 { 476 tagKs = append(tagKs, ks[i].shift(x)) 477 } 478 } 479 } 480 481 return e.teeHole(tagKs...) 482 }