github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/cmd/compile/internal/gc/closure.go (about) 1 // Copyright 2009 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 gc 6 7 import ( 8 "cmd/compile/internal/syntax" 9 "cmd/compile/internal/types" 10 "fmt" 11 ) 12 13 func (p *noder) funcLit(expr *syntax.FuncLit) *Node { 14 xtype := p.typeExpr(expr.Type) 15 ntype := p.typeExpr(expr.Type) 16 17 xfunc := p.nod(expr, ODCLFUNC, nil, nil) 18 xfunc.Func.SetIsHiddenClosure(Curfn != nil) 19 xfunc.Func.Nname = newfuncnamel(p.pos(expr), nblank.Sym) // filled in by typecheckclosure 20 xfunc.Func.Nname.Name.Param.Ntype = xtype 21 xfunc.Func.Nname.Name.Defn = xfunc 22 23 clo := p.nod(expr, OCLOSURE, nil, nil) 24 clo.Func.Ntype = ntype 25 26 xfunc.Func.Closure = clo 27 clo.Func.Closure = xfunc 28 29 p.funcBody(xfunc, expr.Body) 30 31 // closure-specific variables are hanging off the 32 // ordinary ones in the symbol table; see oldname. 33 // unhook them. 34 // make the list of pointers for the closure call. 35 for _, v := range xfunc.Func.Cvars.Slice() { 36 // Unlink from v1; see comment in syntax.go type Param for these fields. 37 v1 := v.Name.Defn 38 v1.Name.Param.Innermost = v.Name.Param.Outer 39 40 // If the closure usage of v is not dense, 41 // we need to make it dense; now that we're out 42 // of the function in which v appeared, 43 // look up v.Sym in the enclosing function 44 // and keep it around for use in the compiled code. 45 // 46 // That is, suppose we just finished parsing the innermost 47 // closure f4 in this code: 48 // 49 // func f() { 50 // v := 1 51 // func() { // f2 52 // use(v) 53 // func() { // f3 54 // func() { // f4 55 // use(v) 56 // }() 57 // }() 58 // }() 59 // } 60 // 61 // At this point v.Outer is f2's v; there is no f3's v. 62 // To construct the closure f4 from within f3, 63 // we need to use f3's v and in this case we need to create f3's v. 64 // We are now in the context of f3, so calling oldname(v.Sym) 65 // obtains f3's v, creating it if necessary (as it is in the example). 66 // 67 // capturevars will decide whether to use v directly or &v. 68 v.Name.Param.Outer = oldname(v.Sym) 69 } 70 71 return clo 72 } 73 74 func typecheckclosure(clo *Node, top int) { 75 xfunc := clo.Func.Closure 76 77 for _, ln := range xfunc.Func.Cvars.Slice() { 78 n := ln.Name.Defn 79 if !n.Name.Captured() { 80 n.Name.SetCaptured(true) 81 if n.Name.Decldepth == 0 { 82 Fatalf("typecheckclosure: var %S does not have decldepth assigned", n) 83 } 84 85 // Ignore assignments to the variable in straightline code 86 // preceding the first capturing by a closure. 87 if n.Name.Decldepth == decldepth { 88 n.SetAssigned(false) 89 } 90 } 91 } 92 93 xfunc.Func.Nname.Sym = closurename(Curfn) 94 disableExport(xfunc.Func.Nname.Sym) 95 declare(xfunc.Func.Nname, PFUNC) 96 xfunc = typecheck(xfunc, ctxStmt) 97 98 clo.Func.Ntype = typecheck(clo.Func.Ntype, Etype) 99 clo.Type = clo.Func.Ntype.Type 100 clo.Func.Top = top 101 102 // Type check the body now, but only if we're inside a function. 103 // At top level (in a variable initialization: curfn==nil) we're not 104 // ready to type check code yet; we'll check it later, because the 105 // underlying closure function we create is added to xtop. 106 if Curfn != nil && clo.Type != nil { 107 oldfn := Curfn 108 Curfn = xfunc 109 olddd := decldepth 110 decldepth = 1 111 typecheckslice(xfunc.Nbody.Slice(), ctxStmt) 112 decldepth = olddd 113 Curfn = oldfn 114 } 115 116 xtop = append(xtop, xfunc) 117 } 118 119 // globClosgen is like Func.Closgen, but for the global scope. 120 var globClosgen int 121 122 // closurename generates a new unique name for a closure within 123 // outerfunc. 124 func closurename(outerfunc *Node) *types.Sym { 125 outer := "glob." 126 prefix := "func" 127 gen := &globClosgen 128 129 if outerfunc != nil { 130 if outerfunc.Func.Closure != nil { 131 prefix = "" 132 } 133 134 outer = outerfunc.funcname() 135 136 // There may be multiple functions named "_". In those 137 // cases, we can't use their individual Closgens as it 138 // would lead to name clashes. 139 if !outerfunc.Func.Nname.isBlank() { 140 gen = &outerfunc.Func.Closgen 141 } 142 } 143 144 *gen++ 145 return lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen)) 146 } 147 148 // capturevarscomplete is set to true when the capturevars phase is done. 149 var capturevarscomplete bool 150 151 // capturevars is called in a separate phase after all typechecking is done. 152 // It decides whether each variable captured by a closure should be captured 153 // by value or by reference. 154 // We use value capturing for values <= 128 bytes that are never reassigned 155 // after capturing (effectively constant). 156 func capturevars(xfunc *Node) { 157 lno := lineno 158 lineno = xfunc.Pos 159 160 clo := xfunc.Func.Closure 161 cvars := xfunc.Func.Cvars.Slice() 162 out := cvars[:0] 163 for _, v := range cvars { 164 if v.Type == nil { 165 // If v.Type is nil, it means v looked like it 166 // was going to be used in the closure, but 167 // isn't. This happens in struct literals like 168 // s{f: x} where we can't distinguish whether 169 // f is a field identifier or expression until 170 // resolving s. 171 continue 172 } 173 out = append(out, v) 174 175 // type check the & of closed variables outside the closure, 176 // so that the outer frame also grabs them and knows they escape. 177 dowidth(v.Type) 178 179 outer := v.Name.Param.Outer 180 outermost := v.Name.Defn 181 182 // out parameters will be assigned to implicitly upon return. 183 if outer.Class() != PPARAMOUT && !outermost.Addrtaken() && !outermost.Assigned() && v.Type.Width <= 128 { 184 v.Name.SetByval(true) 185 } else { 186 outermost.SetAddrtaken(true) 187 outer = nod(OADDR, outer, nil) 188 } 189 190 if Debug['m'] > 1 { 191 var name *types.Sym 192 if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil { 193 name = v.Name.Curfn.Func.Nname.Sym 194 } 195 how := "ref" 196 if v.Name.Byval() { 197 how = "value" 198 } 199 Warnl(v.Pos, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, outermost.Addrtaken(), outermost.Assigned(), int32(v.Type.Width)) 200 } 201 202 outer = typecheck(outer, ctxExpr) 203 clo.Func.Enter.Append(outer) 204 } 205 206 xfunc.Func.Cvars.Set(out) 207 lineno = lno 208 } 209 210 // transformclosure is called in a separate phase after escape analysis. 211 // It transform closure bodies to properly reference captured variables. 212 func transformclosure(xfunc *Node) { 213 lno := lineno 214 lineno = xfunc.Pos 215 clo := xfunc.Func.Closure 216 217 if clo.Func.Top&ctxCallee != 0 { 218 // If the closure is directly called, we transform it to a plain function call 219 // with variables passed as args. This avoids allocation of a closure object. 220 // Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE) 221 // will complete the transformation later. 222 // For illustration, the following closure: 223 // func(a int) { 224 // println(byval) 225 // byref++ 226 // }(42) 227 // becomes: 228 // func(byval int, &byref *int, a int) { 229 // println(byval) 230 // (*&byref)++ 231 // }(byval, &byref, 42) 232 233 // f is ONAME of the actual function. 234 f := xfunc.Func.Nname 235 236 // We are going to insert captured variables before input args. 237 var params []*types.Field 238 var decls []*Node 239 for _, v := range xfunc.Func.Cvars.Slice() { 240 if !v.Name.Byval() { 241 // If v of type T is captured by reference, 242 // we introduce function param &v *T 243 // and v remains PAUTOHEAP with &v heapaddr 244 // (accesses will implicitly deref &v). 245 addr := newname(lookup("&" + v.Sym.Name)) 246 addr.Type = types.NewPtr(v.Type) 247 v.Name.Param.Heapaddr = addr 248 v = addr 249 } 250 251 v.SetClass(PPARAM) 252 decls = append(decls, v) 253 254 fld := types.NewField() 255 fld.Nname = asTypesNode(v) 256 fld.Type = v.Type 257 fld.Sym = v.Sym 258 params = append(params, fld) 259 } 260 261 if len(params) > 0 { 262 // Prepend params and decls. 263 f.Type.Params().SetFields(append(params, f.Type.Params().FieldSlice()...)) 264 xfunc.Func.Dcl = append(decls, xfunc.Func.Dcl...) 265 } 266 267 dowidth(f.Type) 268 xfunc.Type = f.Type // update type of ODCLFUNC 269 } else { 270 // The closure is not called, so it is going to stay as closure. 271 var body []*Node 272 offset := int64(Widthptr) 273 for _, v := range xfunc.Func.Cvars.Slice() { 274 // cv refers to the field inside of closure OSTRUCTLIT. 275 cv := nod(OCLOSUREVAR, nil, nil) 276 277 cv.Type = v.Type 278 if !v.Name.Byval() { 279 cv.Type = types.NewPtr(v.Type) 280 } 281 offset = Rnd(offset, int64(cv.Type.Align)) 282 cv.Xoffset = offset 283 offset += cv.Type.Width 284 285 if v.Name.Byval() && v.Type.Width <= int64(2*Widthptr) { 286 // If it is a small variable captured by value, downgrade it to PAUTO. 287 v.SetClass(PAUTO) 288 xfunc.Func.Dcl = append(xfunc.Func.Dcl, v) 289 body = append(body, nod(OAS, v, cv)) 290 } else { 291 // Declare variable holding addresses taken from closure 292 // and initialize in entry prologue. 293 addr := newname(lookup("&" + v.Sym.Name)) 294 addr.Type = types.NewPtr(v.Type) 295 addr.SetClass(PAUTO) 296 addr.Name.SetUsed(true) 297 addr.Name.Curfn = xfunc 298 xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr) 299 v.Name.Param.Heapaddr = addr 300 if v.Name.Byval() { 301 cv = nod(OADDR, cv, nil) 302 } 303 body = append(body, nod(OAS, addr, cv)) 304 } 305 } 306 307 if len(body) > 0 { 308 typecheckslice(body, ctxStmt) 309 xfunc.Func.Enter.Set(body) 310 xfunc.Func.SetNeedctxt(true) 311 } 312 } 313 314 lineno = lno 315 } 316 317 // hasemptycvars reports whether closure clo has an 318 // empty list of captured vars. 319 func hasemptycvars(clo *Node) bool { 320 xfunc := clo.Func.Closure 321 return xfunc.Func.Cvars.Len() == 0 322 } 323 324 // closuredebugruntimecheck applies boilerplate checks for debug flags 325 // and compiling runtime 326 func closuredebugruntimecheck(clo *Node) { 327 if Debug_closure > 0 { 328 xfunc := clo.Func.Closure 329 if clo.Esc == EscHeap { 330 Warnl(clo.Pos, "heap closure, captured vars = %v", xfunc.Func.Cvars) 331 } else { 332 Warnl(clo.Pos, "stack closure, captured vars = %v", xfunc.Func.Cvars) 333 } 334 } 335 if compiling_runtime && clo.Esc == EscHeap { 336 yyerrorl(clo.Pos, "heap-allocated closure, not allowed in runtime.") 337 } 338 } 339 340 // closureType returns the struct type used to hold all the information 341 // needed in the closure for clo (clo must be a OCLOSURE node). 342 // The address of a variable of the returned type can be cast to a func. 343 func closureType(clo *Node) *types.Type { 344 // Create closure in the form of a composite literal. 345 // supposing the closure captures an int i and a string s 346 // and has one float64 argument and no results, 347 // the generated code looks like: 348 // 349 // clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s} 350 // 351 // The use of the struct provides type information to the garbage 352 // collector so that it can walk the closure. We could use (in this case) 353 // [3]unsafe.Pointer instead, but that would leave the gc in the dark. 354 // The information appears in the binary in the form of type descriptors; 355 // the struct is unnamed so that closures in multiple packages with the 356 // same struct type can share the descriptor. 357 fields := []*Node{ 358 namedfield(".F", types.Types[TUINTPTR]), 359 } 360 for _, v := range clo.Func.Closure.Func.Cvars.Slice() { 361 typ := v.Type 362 if !v.Name.Byval() { 363 typ = types.NewPtr(typ) 364 } 365 fields = append(fields, symfield(v.Sym, typ)) 366 } 367 typ := tostruct(fields) 368 typ.SetNoalg(true) 369 return typ 370 } 371 372 func walkclosure(clo *Node, init *Nodes) *Node { 373 xfunc := clo.Func.Closure 374 375 // If no closure vars, don't bother wrapping. 376 if hasemptycvars(clo) { 377 if Debug_closure > 0 { 378 Warnl(clo.Pos, "closure converted to global") 379 } 380 return xfunc.Func.Nname 381 } 382 closuredebugruntimecheck(clo) 383 384 typ := closureType(clo) 385 386 clos := nod(OCOMPLIT, nil, nod(ODEREF, typenod(typ), nil)) 387 clos.Esc = clo.Esc 388 clos.Right.SetImplicit(true) 389 clos.List.Set(append([]*Node{nod(OCFUNC, xfunc.Func.Nname, nil)}, clo.Func.Enter.Slice()...)) 390 391 // Force type conversion from *struct to the func type. 392 clos = convnop(clos, clo.Type) 393 394 // typecheck will insert a PTRLIT node under CONVNOP, 395 // tag it with escape analysis result. 396 clos.Left.Esc = clo.Esc 397 398 // non-escaping temp to use, if any. 399 if x := prealloc[clo]; x != nil { 400 if !types.Identical(typ, x.Type) { 401 panic("closure type does not match order's assigned type") 402 } 403 clos.Left.Right = x 404 delete(prealloc, clo) 405 } 406 407 return walkexpr(clos, init) 408 } 409 410 func typecheckpartialcall(fn *Node, sym *types.Sym) { 411 switch fn.Op { 412 case ODOTINTER, ODOTMETH: 413 break 414 415 default: 416 Fatalf("invalid typecheckpartialcall") 417 } 418 419 // Create top-level function. 420 xfunc := makepartialcall(fn, fn.Type, sym) 421 fn.Func = xfunc.Func 422 fn.Right = newname(sym) 423 fn.Op = OCALLPART 424 fn.Type = xfunc.Type 425 } 426 427 func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node { 428 rcvrtype := fn.Left.Type 429 sym := methodSymSuffix(rcvrtype, meth, "-fm") 430 431 if sym.Uniq() { 432 return asNode(sym.Def) 433 } 434 sym.SetUniq(true) 435 436 savecurfn := Curfn 437 saveLineNo := lineno 438 Curfn = nil 439 440 // Set line number equal to the line number where the method is declared. 441 var m *types.Field 442 if lookdot0(meth, rcvrtype, &m, false) == 1 && m.Pos.IsKnown() { 443 lineno = m.Pos 444 } 445 // Note: !m.Pos.IsKnown() happens for method expressions where 446 // the method is implicitly declared. The Error method of the 447 // built-in error type is one such method. We leave the line 448 // number at the use of the method expression in this 449 // case. See issue 29389. 450 451 tfn := nod(OTFUNC, nil, nil) 452 tfn.List.Set(structargs(t0.Params(), true)) 453 tfn.Rlist.Set(structargs(t0.Results(), false)) 454 455 disableExport(sym) 456 xfunc := dclfunc(sym, tfn) 457 xfunc.Func.SetDupok(true) 458 xfunc.Func.SetNeedctxt(true) 459 460 tfn.Type.SetPkg(t0.Pkg()) 461 462 // Declare and initialize variable holding receiver. 463 464 cv := nod(OCLOSUREVAR, nil, nil) 465 cv.Type = rcvrtype 466 cv.Xoffset = Rnd(int64(Widthptr), int64(cv.Type.Align)) 467 468 ptr := newname(lookup(".this")) 469 declare(ptr, PAUTO) 470 ptr.Name.SetUsed(true) 471 var body []*Node 472 if rcvrtype.IsPtr() || rcvrtype.IsInterface() { 473 ptr.Type = rcvrtype 474 body = append(body, nod(OAS, ptr, cv)) 475 } else { 476 ptr.Type = types.NewPtr(rcvrtype) 477 body = append(body, nod(OAS, ptr, nod(OADDR, cv, nil))) 478 } 479 480 call := nod(OCALL, nodSym(OXDOT, ptr, meth), nil) 481 call.List.Set(paramNnames(tfn.Type)) 482 call.SetIsDDD(tfn.Type.IsVariadic()) 483 if t0.NumResults() != 0 { 484 n := nod(ORETURN, nil, nil) 485 n.List.Set1(call) 486 call = n 487 } 488 body = append(body, call) 489 490 xfunc.Nbody.Set(body) 491 funcbody() 492 493 xfunc = typecheck(xfunc, ctxStmt) 494 sym.Def = asTypesNode(xfunc) 495 xtop = append(xtop, xfunc) 496 Curfn = savecurfn 497 lineno = saveLineNo 498 499 return xfunc 500 } 501 502 // partialCallType returns the struct type used to hold all the information 503 // needed in the closure for n (n must be a OCALLPART node). 504 // The address of a variable of the returned type can be cast to a func. 505 func partialCallType(n *Node) *types.Type { 506 t := tostruct([]*Node{ 507 namedfield("F", types.Types[TUINTPTR]), 508 namedfield("R", n.Left.Type), 509 }) 510 t.SetNoalg(true) 511 return t 512 } 513 514 func walkpartialcall(n *Node, init *Nodes) *Node { 515 // Create closure in the form of a composite literal. 516 // For x.M with receiver (x) type T, the generated code looks like: 517 // 518 // clos = &struct{F uintptr; R T}{M.T·f, x} 519 // 520 // Like walkclosure above. 521 522 if n.Left.Type.IsInterface() { 523 // Trigger panic for method on nil interface now. 524 // Otherwise it happens in the wrapper and is confusing. 525 n.Left = cheapexpr(n.Left, init) 526 527 checknil(n.Left, init) 528 } 529 530 typ := partialCallType(n) 531 532 clos := nod(OCOMPLIT, nil, nod(ODEREF, typenod(typ), nil)) 533 clos.Esc = n.Esc 534 clos.Right.SetImplicit(true) 535 clos.List.Set2(nod(OCFUNC, n.Func.Nname, nil), n.Left) 536 537 // Force type conversion from *struct to the func type. 538 clos = convnop(clos, n.Type) 539 540 // The typecheck inside convnop will insert a PTRLIT node under CONVNOP. 541 // Tag it with escape analysis result. 542 clos.Left.Esc = n.Esc 543 544 // non-escaping temp to use, if any. 545 if x := prealloc[n]; x != nil { 546 if !types.Identical(typ, x.Type) { 547 panic("partial call type does not match order's assigned type") 548 } 549 clos.Left.Right = x 550 delete(prealloc, n) 551 } 552 553 return walkexpr(clos, init) 554 }