github.com/huandu/go@v0.0.0-20151114150818-04e615e41150/src/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/internal/obj" 9 "fmt" 10 ) 11 12 /* 13 * function literals aka closures 14 */ 15 func closurehdr(ntype *Node) { 16 var name *Node 17 var a *Node 18 19 n := Nod(OCLOSURE, nil, nil) 20 n.Func.Ntype = ntype 21 n.Func.Depth = Funcdepth 22 n.Func.Outerfunc = Curfn 23 24 funchdr(n) 25 26 // steal ntype's argument names and 27 // leave a fresh copy in their place. 28 // references to these variables need to 29 // refer to the variables in the external 30 // function declared below; see walkclosure. 31 n.List = ntype.List 32 33 n.Rlist = ntype.Rlist 34 ntype.List = nil 35 ntype.Rlist = nil 36 for l := n.List; l != nil; l = l.Next { 37 name = l.N.Left 38 if name != nil { 39 name = newname(name.Sym) 40 } 41 a = Nod(ODCLFIELD, name, l.N.Right) 42 a.Isddd = l.N.Isddd 43 if name != nil { 44 name.Isddd = a.Isddd 45 } 46 ntype.List = list(ntype.List, a) 47 } 48 49 for l := n.Rlist; l != nil; l = l.Next { 50 name = l.N.Left 51 if name != nil { 52 name = newname(name.Sym) 53 } 54 ntype.Rlist = list(ntype.Rlist, Nod(ODCLFIELD, name, l.N.Right)) 55 } 56 } 57 58 func closurebody(body *NodeList) *Node { 59 if body == nil { 60 body = list1(Nod(OEMPTY, nil, nil)) 61 } 62 63 func_ := Curfn 64 func_.Nbody = body 65 func_.Func.Endlineno = lineno 66 funcbody(func_) 67 68 // closure-specific variables are hanging off the 69 // ordinary ones in the symbol table; see oldname. 70 // unhook them. 71 // make the list of pointers for the closure call. 72 var v *Node 73 for l := func_.Func.Cvars; l != nil; l = l.Next { 74 v = l.N 75 v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer 76 v.Name.Param.Outerexpr = oldname(v.Sym) 77 } 78 79 return func_ 80 } 81 82 func typecheckclosure(func_ *Node, top int) { 83 var n *Node 84 85 for l := func_.Func.Cvars; l != nil; l = l.Next { 86 n = l.N.Name.Param.Closure 87 if !n.Name.Captured { 88 n.Name.Captured = true 89 if n.Name.Decldepth == 0 { 90 Fatal("typecheckclosure: var %v does not have decldepth assigned", Nconv(n, obj.FmtShort)) 91 } 92 93 // Ignore assignments to the variable in straightline code 94 // preceding the first capturing by a closure. 95 if n.Name.Decldepth == decldepth { 96 n.Assigned = false 97 } 98 } 99 } 100 101 for l := func_.Func.Dcl; l != nil; l = l.Next { 102 if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) { 103 l.N.Name.Decldepth = 1 104 } 105 } 106 107 oldfn := Curfn 108 typecheck(&func_.Func.Ntype, Etype) 109 func_.Type = func_.Func.Ntype.Type 110 func_.Func.Top = top 111 112 // Type check the body now, but only if we're inside a function. 113 // At top level (in a variable initialization: curfn==nil) we're not 114 // ready to type check code yet; we'll check it later, because the 115 // underlying closure function we create is added to xtop. 116 if Curfn != nil && func_.Type != nil { 117 Curfn = func_ 118 olddd := decldepth 119 decldepth = 1 120 typechecklist(func_.Nbody, Etop) 121 decldepth = olddd 122 Curfn = oldfn 123 } 124 125 // Create top-level function 126 xtop = list(xtop, makeclosure(func_)) 127 } 128 129 // closurename returns name for OCLOSURE n. 130 // It is not as simple as it ought to be, because we typecheck nested closures 131 // starting from the innermost one. So when we check the inner closure, 132 // we don't yet have name for the outer closure. This function uses recursion 133 // to generate names all the way up if necessary. 134 135 var closurename_closgen int 136 137 func closurename(n *Node) *Sym { 138 if n.Sym != nil { 139 return n.Sym 140 } 141 gen := 0 142 outer := "" 143 prefix := "" 144 if n.Func.Outerfunc == nil { 145 // Global closure. 146 outer = "glob" 147 148 prefix = "func" 149 closurename_closgen++ 150 gen = closurename_closgen 151 } else if n.Func.Outerfunc.Op == ODCLFUNC { 152 // The outermost closure inside of a named function. 153 outer = n.Func.Outerfunc.Func.Nname.Sym.Name 154 155 prefix = "func" 156 157 // Yes, functions can be named _. 158 // Can't use function closgen in such case, 159 // because it would lead to name clashes. 160 if !isblank(n.Func.Outerfunc.Func.Nname) { 161 n.Func.Outerfunc.Func.Closgen++ 162 gen = n.Func.Outerfunc.Func.Closgen 163 } else { 164 closurename_closgen++ 165 gen = closurename_closgen 166 } 167 } else if n.Func.Outerfunc.Op == OCLOSURE { 168 // Nested closure, recurse. 169 outer = closurename(n.Func.Outerfunc).Name 170 171 prefix = "" 172 n.Func.Outerfunc.Func.Closgen++ 173 gen = n.Func.Outerfunc.Func.Closgen 174 } else { 175 Fatal("closurename called for %v", Nconv(n, obj.FmtShort)) 176 } 177 n.Sym = Lookupf("%s.%s%d", outer, prefix, gen) 178 return n.Sym 179 } 180 181 func makeclosure(func_ *Node) *Node { 182 /* 183 * wrap body in external function 184 * that begins by reading closure parameters. 185 */ 186 xtype := Nod(OTFUNC, nil, nil) 187 188 xtype.List = func_.List 189 xtype.Rlist = func_.Rlist 190 191 // create the function 192 xfunc := Nod(ODCLFUNC, nil, nil) 193 194 xfunc.Func.Nname = newfuncname(closurename(func_)) 195 xfunc.Func.Nname.Sym.Flags |= SymExported // disable export 196 xfunc.Func.Nname.Name.Param.Ntype = xtype 197 xfunc.Func.Nname.Name.Defn = xfunc 198 declare(xfunc.Func.Nname, PFUNC) 199 xfunc.Func.Nname.Name.Funcdepth = func_.Func.Depth 200 xfunc.Func.Depth = func_.Func.Depth 201 xfunc.Func.Endlineno = func_.Func.Endlineno 202 makefuncsym(xfunc.Func.Nname.Sym) 203 204 xfunc.Nbody = func_.Nbody 205 xfunc.Func.Dcl = concat(func_.Func.Dcl, xfunc.Func.Dcl) 206 if xfunc.Nbody == nil { 207 Fatal("empty body - won't generate any code") 208 } 209 typecheck(&xfunc, Etop) 210 211 xfunc.Func.Closure = func_ 212 func_.Func.Closure = xfunc 213 214 func_.Nbody = nil 215 func_.List = nil 216 func_.Rlist = nil 217 218 return xfunc 219 } 220 221 // capturevars is called in a separate phase after all typechecking is done. 222 // It decides whether each variable captured by a closure should be captured 223 // by value or by reference. 224 // We use value capturing for values <= 128 bytes that are never reassigned 225 // after capturing (effectively constant). 226 func capturevars(xfunc *Node) { 227 var v *Node 228 var outer *Node 229 230 lno := int(lineno) 231 lineno = xfunc.Lineno 232 233 func_ := xfunc.Func.Closure 234 func_.Func.Enter = nil 235 for l := func_.Func.Cvars; l != nil; l = l.Next { 236 v = l.N 237 if v.Type == nil { 238 // if v->type is nil, it means v looked like it was 239 // going to be used in the closure but wasn't. 240 // this happens because when parsing a, b, c := f() 241 // the a, b, c gets parsed as references to older 242 // a, b, c before the parser figures out this is a 243 // declaration. 244 v.Op = OXXX 245 246 continue 247 } 248 249 // type check the & of closed variables outside the closure, 250 // so that the outer frame also grabs them and knows they escape. 251 dowidth(v.Type) 252 253 outer = v.Name.Param.Outerexpr 254 v.Name.Param.Outerexpr = nil 255 256 // out parameters will be assigned to implicitly upon return. 257 if outer.Class != PPARAMOUT && !v.Name.Param.Closure.Addrtaken && !v.Name.Param.Closure.Assigned && v.Type.Width <= 128 { 258 v.Name.Byval = true 259 } else { 260 v.Name.Param.Closure.Addrtaken = true 261 outer = Nod(OADDR, outer, nil) 262 } 263 264 if Debug['m'] > 1 { 265 var name *Sym 266 if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil { 267 name = v.Name.Curfn.Func.Nname.Sym 268 } 269 how := "ref" 270 if v.Name.Byval { 271 how = "value" 272 } 273 Warnl(int(v.Lineno), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, v.Name.Param.Closure.Addrtaken, v.Name.Param.Closure.Assigned, int32(v.Type.Width)) 274 } 275 276 typecheck(&outer, Erv) 277 func_.Func.Enter = list(func_.Func.Enter, outer) 278 } 279 280 lineno = int32(lno) 281 } 282 283 // transformclosure is called in a separate phase after escape analysis. 284 // It transform closure bodies to properly reference captured variables. 285 func transformclosure(xfunc *Node) { 286 lno := int(lineno) 287 lineno = xfunc.Lineno 288 func_ := xfunc.Func.Closure 289 290 if func_.Func.Top&Ecall != 0 { 291 // If the closure is directly called, we transform it to a plain function call 292 // with variables passed as args. This avoids allocation of a closure object. 293 // Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE) 294 // will complete the transformation later. 295 // For illustration, the following closure: 296 // func(a int) { 297 // println(byval) 298 // byref++ 299 // }(42) 300 // becomes: 301 // func(a int, byval int, &byref *int) { 302 // println(byval) 303 // (*&byref)++ 304 // }(byval, &byref, 42) 305 306 // f is ONAME of the actual function. 307 f := xfunc.Func.Nname 308 309 // Get pointer to input arguments. 310 // We are going to insert captured variables before input args. 311 param := &getinargx(f.Type).Type 312 original_args := *param // old input args 313 original_dcl := xfunc.Func.Dcl 314 xfunc.Func.Dcl = nil 315 316 var v *Node 317 var addr *Node 318 var fld *Type 319 for l := func_.Func.Cvars; l != nil; l = l.Next { 320 v = l.N 321 if v.Op == OXXX { 322 continue 323 } 324 fld = typ(TFIELD) 325 fld.Funarg = 1 326 if v.Name.Byval { 327 // If v is captured by value, we merely downgrade it to PPARAM. 328 v.Class = PPARAM 329 330 v.Ullman = 1 331 fld.Nname = v 332 } else { 333 // If v of type T is captured by reference, 334 // we introduce function param &v *T 335 // and v remains PPARAMREF with &v heapaddr 336 // (accesses will implicitly deref &v). 337 addr = newname(Lookupf("&%s", v.Sym.Name)) 338 addr.Type = Ptrto(v.Type) 339 addr.Class = PPARAM 340 v.Name.Heapaddr = addr 341 fld.Nname = addr 342 } 343 344 fld.Type = fld.Nname.Type 345 fld.Sym = fld.Nname.Sym 346 347 // Declare the new param and add it the first part of the input arguments. 348 xfunc.Func.Dcl = list(xfunc.Func.Dcl, fld.Nname) 349 350 *param = fld 351 param = &fld.Down 352 } 353 *param = original_args 354 xfunc.Func.Dcl = concat(xfunc.Func.Dcl, original_dcl) 355 356 // Recalculate param offsets. 357 if f.Type.Width > 0 { 358 Fatal("transformclosure: width is already calculated") 359 } 360 dowidth(f.Type) 361 xfunc.Type = f.Type // update type of ODCLFUNC 362 } else { 363 // The closure is not called, so it is going to stay as closure. 364 nvar := 0 365 366 var body *NodeList 367 offset := int64(Widthptr) 368 var addr *Node 369 var v *Node 370 var cv *Node 371 for l := func_.Func.Cvars; l != nil; l = l.Next { 372 v = l.N 373 if v.Op == OXXX { 374 continue 375 } 376 nvar++ 377 378 // cv refers to the field inside of closure OSTRUCTLIT. 379 cv = Nod(OCLOSUREVAR, nil, nil) 380 381 cv.Type = v.Type 382 if !v.Name.Byval { 383 cv.Type = Ptrto(v.Type) 384 } 385 offset = Rnd(offset, int64(cv.Type.Align)) 386 cv.Xoffset = offset 387 offset += cv.Type.Width 388 389 if v.Name.Byval && v.Type.Width <= int64(2*Widthptr) { 390 // If it is a small variable captured by value, downgrade it to PAUTO. 391 v.Class = PAUTO 392 v.Ullman = 1 393 xfunc.Func.Dcl = list(xfunc.Func.Dcl, v) 394 body = list(body, Nod(OAS, v, cv)) 395 } else { 396 // Declare variable holding addresses taken from closure 397 // and initialize in entry prologue. 398 addr = newname(Lookupf("&%s", v.Sym.Name)) 399 addr.Name.Param.Ntype = Nod(OIND, typenod(v.Type), nil) 400 addr.Class = PAUTO 401 addr.Used = true 402 addr.Name.Curfn = xfunc 403 xfunc.Func.Dcl = list(xfunc.Func.Dcl, addr) 404 v.Name.Heapaddr = addr 405 if v.Name.Byval { 406 cv = Nod(OADDR, cv, nil) 407 } 408 body = list(body, Nod(OAS, addr, cv)) 409 } 410 } 411 412 typechecklist(body, Etop) 413 walkstmtlist(body) 414 xfunc.Func.Enter = body 415 xfunc.Func.Needctxt = nvar > 0 416 } 417 418 lineno = int32(lno) 419 } 420 421 func walkclosure(func_ *Node, init **NodeList) *Node { 422 // If no closure vars, don't bother wrapping. 423 if func_.Func.Cvars == nil { 424 return func_.Func.Closure.Func.Nname 425 } 426 427 // Create closure in the form of a composite literal. 428 // supposing the closure captures an int i and a string s 429 // and has one float64 argument and no results, 430 // the generated code looks like: 431 // 432 // clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s} 433 // 434 // The use of the struct provides type information to the garbage 435 // collector so that it can walk the closure. We could use (in this case) 436 // [3]unsafe.Pointer instead, but that would leave the gc in the dark. 437 // The information appears in the binary in the form of type descriptors; 438 // the struct is unnamed so that closures in multiple packages with the 439 // same struct type can share the descriptor. 440 441 typ := Nod(OTSTRUCT, nil, nil) 442 443 typ.List = list1(Nod(ODCLFIELD, newname(Lookup(".F")), typenod(Types[TUINTPTR]))) 444 var typ1 *Node 445 var v *Node 446 for l := func_.Func.Cvars; l != nil; l = l.Next { 447 v = l.N 448 if v.Op == OXXX { 449 continue 450 } 451 typ1 = typenod(v.Type) 452 if !v.Name.Byval { 453 typ1 = Nod(OIND, typ1, nil) 454 } 455 typ.List = list(typ.List, Nod(ODCLFIELD, newname(v.Sym), typ1)) 456 } 457 458 clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil)) 459 clos.Esc = func_.Esc 460 clos.Right.Implicit = true 461 clos.List = concat(list1(Nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)), func_.Func.Enter) 462 463 // Force type conversion from *struct to the func type. 464 clos = Nod(OCONVNOP, clos, nil) 465 466 clos.Type = func_.Type 467 468 typecheck(&clos, Erv) 469 470 // typecheck will insert a PTRLIT node under CONVNOP, 471 // tag it with escape analysis result. 472 clos.Left.Esc = func_.Esc 473 474 // non-escaping temp to use, if any. 475 // orderexpr did not compute the type; fill it in now. 476 if x := prealloc[func_]; x != nil { 477 x.Type = clos.Left.Left.Type 478 x.Orig.Type = x.Type 479 clos.Left.Right = x 480 delete(prealloc, func_) 481 } 482 483 walkexpr(&clos, init) 484 485 return clos 486 } 487 488 func typecheckpartialcall(fn *Node, sym *Node) { 489 switch fn.Op { 490 case ODOTINTER, ODOTMETH: 491 break 492 493 default: 494 Fatal("invalid typecheckpartialcall") 495 } 496 497 // Create top-level function. 498 xfunc := makepartialcall(fn, fn.Type, sym) 499 fn.Func = xfunc.Func 500 fn.Right = sym 501 fn.Op = OCALLPART 502 fn.Type = xfunc.Type 503 } 504 505 var makepartialcall_gopkg *Pkg 506 507 func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node { 508 var p string 509 510 rcvrtype := fn.Left.Type 511 if exportname(meth.Sym.Name) { 512 p = fmt.Sprintf("(%v).%s-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), meth.Sym.Name) 513 } else { 514 p = fmt.Sprintf("(%v).(%v)-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), Sconv(meth.Sym, obj.FmtLeft)) 515 } 516 basetype := rcvrtype 517 if Isptr[rcvrtype.Etype] { 518 basetype = basetype.Type 519 } 520 if basetype.Etype != TINTER && basetype.Sym == nil { 521 Fatal("missing base type for %v", rcvrtype) 522 } 523 524 var spkg *Pkg 525 if basetype.Sym != nil { 526 spkg = basetype.Sym.Pkg 527 } 528 if spkg == nil { 529 if makepartialcall_gopkg == nil { 530 makepartialcall_gopkg = mkpkg("go") 531 } 532 spkg = makepartialcall_gopkg 533 } 534 535 sym := Pkglookup(p, spkg) 536 537 if sym.Flags&SymUniq != 0 { 538 return sym.Def 539 } 540 sym.Flags |= SymUniq 541 542 savecurfn := Curfn 543 Curfn = nil 544 545 xtype := Nod(OTFUNC, nil, nil) 546 i := 0 547 var l *NodeList 548 var callargs *NodeList 549 ddd := false 550 xfunc := Nod(ODCLFUNC, nil, nil) 551 Curfn = xfunc 552 var fld *Node 553 var n *Node 554 for t := getinargx(t0).Type; t != nil; t = t.Down { 555 n = newname(Lookupf("a%d", i)) 556 i++ 557 n.Class = PPARAM 558 xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) 559 callargs = list(callargs, n) 560 fld = Nod(ODCLFIELD, n, typenod(t.Type)) 561 if t.Isddd { 562 fld.Isddd = true 563 ddd = true 564 } 565 566 l = list(l, fld) 567 } 568 569 xtype.List = l 570 i = 0 571 l = nil 572 var retargs *NodeList 573 for t := getoutargx(t0).Type; t != nil; t = t.Down { 574 n = newname(Lookupf("r%d", i)) 575 i++ 576 n.Class = PPARAMOUT 577 xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) 578 retargs = list(retargs, n) 579 l = list(l, Nod(ODCLFIELD, n, typenod(t.Type))) 580 } 581 582 xtype.Rlist = l 583 584 xfunc.Func.Dupok = true 585 xfunc.Func.Nname = newfuncname(sym) 586 xfunc.Func.Nname.Sym.Flags |= SymExported // disable export 587 xfunc.Func.Nname.Name.Param.Ntype = xtype 588 xfunc.Func.Nname.Name.Defn = xfunc 589 declare(xfunc.Func.Nname, PFUNC) 590 591 // Declare and initialize variable holding receiver. 592 593 xfunc.Func.Needctxt = true 594 cv := Nod(OCLOSUREVAR, nil, nil) 595 cv.Xoffset = int64(Widthptr) 596 cv.Type = rcvrtype 597 if int(cv.Type.Align) > Widthptr { 598 cv.Xoffset = int64(cv.Type.Align) 599 } 600 ptr := Nod(ONAME, nil, nil) 601 ptr.Sym = Lookup("rcvr") 602 ptr.Class = PAUTO 603 ptr.Addable = true 604 ptr.Ullman = 1 605 ptr.Used = true 606 ptr.Name.Curfn = xfunc 607 xfunc.Func.Dcl = list(xfunc.Func.Dcl, ptr) 608 var body *NodeList 609 if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) { 610 ptr.Name.Param.Ntype = typenod(rcvrtype) 611 body = list(body, Nod(OAS, ptr, cv)) 612 } else { 613 ptr.Name.Param.Ntype = typenod(Ptrto(rcvrtype)) 614 body = list(body, Nod(OAS, ptr, Nod(OADDR, cv, nil))) 615 } 616 617 call := Nod(OCALL, Nod(OXDOT, ptr, meth), nil) 618 call.List = callargs 619 call.Isddd = ddd 620 if t0.Outtuple == 0 { 621 body = list(body, call) 622 } else { 623 n := Nod(OAS2, nil, nil) 624 n.List = retargs 625 n.Rlist = list1(call) 626 body = list(body, n) 627 n = Nod(ORETURN, nil, nil) 628 body = list(body, n) 629 } 630 631 xfunc.Nbody = body 632 633 typecheck(&xfunc, Etop) 634 sym.Def = xfunc 635 xtop = list(xtop, xfunc) 636 Curfn = savecurfn 637 638 return xfunc 639 } 640 641 func walkpartialcall(n *Node, init **NodeList) *Node { 642 // Create closure in the form of a composite literal. 643 // For x.M with receiver (x) type T, the generated code looks like: 644 // 645 // clos = &struct{F uintptr; R T}{M.T·f, x} 646 // 647 // Like walkclosure above. 648 649 if Isinter(n.Left.Type) { 650 // Trigger panic for method on nil interface now. 651 // Otherwise it happens in the wrapper and is confusing. 652 n.Left = cheapexpr(n.Left, init) 653 654 checknil(n.Left, init) 655 } 656 657 typ := Nod(OTSTRUCT, nil, nil) 658 typ.List = list1(Nod(ODCLFIELD, newname(Lookup("F")), typenod(Types[TUINTPTR]))) 659 typ.List = list(typ.List, Nod(ODCLFIELD, newname(Lookup("R")), typenod(n.Left.Type))) 660 661 clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil)) 662 clos.Esc = n.Esc 663 clos.Right.Implicit = true 664 clos.List = list1(Nod(OCFUNC, n.Func.Nname, nil)) 665 clos.List = list(clos.List, n.Left) 666 667 // Force type conversion from *struct to the func type. 668 clos = Nod(OCONVNOP, clos, nil) 669 670 clos.Type = n.Type 671 672 typecheck(&clos, Erv) 673 674 // typecheck will insert a PTRLIT node under CONVNOP, 675 // tag it with escape analysis result. 676 clos.Left.Esc = n.Esc 677 678 // non-escaping temp to use, if any. 679 // orderexpr did not compute the type; fill it in now. 680 if x := prealloc[n]; x != nil { 681 x.Type = clos.Left.Left.Type 682 x.Orig.Type = x.Type 683 clos.Left.Right = x 684 delete(prealloc, n) 685 } 686 687 walkexpr(&clos, init) 688 689 return clos 690 }