github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/cmd/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.Ntype = ntype 21 n.Funcdepth = 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.Closure.Closure = v.Outer 76 v.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.Closure 87 if !n.Captured { 88 n.Captured = true 89 if n.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.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.Decldepth = 1 104 } 105 } 106 107 oldfn := Curfn 108 typecheck(&func_.Ntype, Etype) 109 func_.Type = func_.Ntype.Type 110 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.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.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.Nname = newfuncname(closurename(func_)) 195 xfunc.Nname.Sym.Flags |= SymExported // disable export 196 xfunc.Nname.Ntype = xtype 197 xfunc.Nname.Defn = xfunc 198 declare(xfunc.Nname, PFUNC) 199 xfunc.Nname.Funcdepth = func_.Funcdepth 200 xfunc.Funcdepth = func_.Funcdepth 201 xfunc.Func.Endlineno = func_.Func.Endlineno 202 203 xfunc.Nbody = func_.Nbody 204 xfunc.Func.Dcl = concat(func_.Func.Dcl, xfunc.Func.Dcl) 205 if xfunc.Nbody == nil { 206 Fatal("empty body - won't generate any code") 207 } 208 typecheck(&xfunc, Etop) 209 210 xfunc.Closure = func_ 211 func_.Closure = xfunc 212 213 func_.Nbody = nil 214 func_.List = nil 215 func_.Rlist = nil 216 217 return xfunc 218 } 219 220 // capturevars is called in a separate phase after all typechecking is done. 221 // It decides whether each variable captured by a closure should be captured 222 // by value or by reference. 223 // We use value capturing for values <= 128 bytes that are never reassigned 224 // after capturing (effectively constant). 225 func capturevars(xfunc *Node) { 226 var v *Node 227 var outer *Node 228 229 lno := int(lineno) 230 lineno = xfunc.Lineno 231 232 func_ := xfunc.Closure 233 func_.Func.Enter = nil 234 for l := func_.Func.Cvars; l != nil; l = l.Next { 235 v = l.N 236 if v.Type == nil { 237 // if v->type is nil, it means v looked like it was 238 // going to be used in the closure but wasn't. 239 // this happens because when parsing a, b, c := f() 240 // the a, b, c gets parsed as references to older 241 // a, b, c before the parser figures out this is a 242 // declaration. 243 v.Op = OXXX 244 245 continue 246 } 247 248 // type check the & of closed variables outside the closure, 249 // so that the outer frame also grabs them and knows they escape. 250 dowidth(v.Type) 251 252 outer = v.Outerexpr 253 v.Outerexpr = nil 254 255 // out parameters will be assigned to implicitly upon return. 256 if outer.Class != PPARAMOUT && !v.Closure.Addrtaken && !v.Closure.Assigned && v.Type.Width <= 128 { 257 v.Byval = true 258 } else { 259 v.Closure.Addrtaken = true 260 outer = Nod(OADDR, outer, nil) 261 } 262 263 if Debug['m'] > 1 { 264 var name *Sym 265 if v.Curfn != nil && v.Curfn.Nname != nil { 266 name = v.Curfn.Nname.Sym 267 } 268 how := "ref" 269 if v.Byval { 270 how = "value" 271 } 272 Warnl(int(v.Lineno), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", Sconv(name, 0), how, Sconv(v.Sym, 0), v.Closure.Addrtaken, v.Closure.Assigned, int32(v.Type.Width)) 273 } 274 275 typecheck(&outer, Erv) 276 func_.Func.Enter = list(func_.Func.Enter, outer) 277 } 278 279 lineno = int32(lno) 280 } 281 282 // transformclosure is called in a separate phase after escape analysis. 283 // It transform closure bodies to properly reference captured variables. 284 func transformclosure(xfunc *Node) { 285 lno := int(lineno) 286 lineno = xfunc.Lineno 287 func_ := xfunc.Closure 288 289 if func_.Top&Ecall != 0 { 290 // If the closure is directly called, we transform it to a plain function call 291 // with variables passed as args. This avoids allocation of a closure object. 292 // Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE) 293 // will complete the transformation later. 294 // For illustration, the following closure: 295 // func(a int) { 296 // println(byval) 297 // byref++ 298 // }(42) 299 // becomes: 300 // func(a int, byval int, &byref *int) { 301 // println(byval) 302 // (*&byref)++ 303 // }(42, byval, &byref) 304 305 // f is ONAME of the actual function. 306 f := xfunc.Nname 307 308 // Get pointer to input arguments and rewind to the end. 309 // We are going to append captured variables to input args. 310 param := &getinargx(f.Type).Type 311 312 for ; *param != nil; param = &(*param).Down { 313 } 314 var v *Node 315 var addr *Node 316 var fld *Type 317 for l := func_.Func.Cvars; l != nil; l = l.Next { 318 v = l.N 319 if v.Op == OXXX { 320 continue 321 } 322 fld = typ(TFIELD) 323 fld.Funarg = 1 324 if v.Byval { 325 // If v is captured by value, we merely downgrade it to PPARAM. 326 v.Class = PPARAM 327 328 v.Ullman = 1 329 fld.Nname = v 330 } else { 331 // If v of type T is captured by reference, 332 // we introduce function param &v *T 333 // and v remains PPARAMREF with &v heapaddr 334 // (accesses will implicitly deref &v). 335 addr = newname(Lookupf("&%s", v.Sym.Name)) 336 addr.Type = Ptrto(v.Type) 337 addr.Class = PPARAM 338 v.Heapaddr = addr 339 fld.Nname = addr 340 } 341 342 fld.Type = fld.Nname.Type 343 fld.Sym = fld.Nname.Sym 344 345 // Declare the new param and append it to input arguments. 346 xfunc.Func.Dcl = list(xfunc.Func.Dcl, fld.Nname) 347 348 *param = fld 349 param = &fld.Down 350 } 351 352 // Recalculate param offsets. 353 if f.Type.Width > 0 { 354 Fatal("transformclosure: width is already calculated") 355 } 356 dowidth(f.Type) 357 xfunc.Type = f.Type // update type of ODCLFUNC 358 } else { 359 // The closure is not called, so it is going to stay as closure. 360 nvar := 0 361 362 var body *NodeList 363 offset := int64(Widthptr) 364 var addr *Node 365 var v *Node 366 var cv *Node 367 for l := func_.Func.Cvars; l != nil; l = l.Next { 368 v = l.N 369 if v.Op == OXXX { 370 continue 371 } 372 nvar++ 373 374 // cv refers to the field inside of closure OSTRUCTLIT. 375 cv = Nod(OCLOSUREVAR, nil, nil) 376 377 cv.Type = v.Type 378 if !v.Byval { 379 cv.Type = Ptrto(v.Type) 380 } 381 offset = Rnd(offset, int64(cv.Type.Align)) 382 cv.Xoffset = offset 383 offset += cv.Type.Width 384 385 if v.Byval && v.Type.Width <= int64(2*Widthptr) && Thearch.Thechar == '6' { 386 // If it is a small variable captured by value, downgrade it to PAUTO. 387 // This optimization is currently enabled only for amd64, see: 388 // https://github.com/golang/go/issues/9865 389 v.Class = PAUTO 390 391 v.Ullman = 1 392 xfunc.Func.Dcl = list(xfunc.Func.Dcl, v) 393 body = list(body, Nod(OAS, v, cv)) 394 } else { 395 // Declare variable holding addresses taken from closure 396 // and initialize in entry prologue. 397 addr = newname(Lookupf("&%s", v.Sym.Name)) 398 addr.Ntype = Nod(OIND, typenod(v.Type), nil) 399 addr.Class = PAUTO 400 addr.Used = true 401 addr.Curfn = xfunc 402 xfunc.Func.Dcl = list(xfunc.Func.Dcl, addr) 403 v.Heapaddr = addr 404 if v.Byval { 405 cv = Nod(OADDR, cv, nil) 406 } 407 body = list(body, Nod(OAS, addr, cv)) 408 } 409 } 410 411 typechecklist(body, Etop) 412 walkstmtlist(body) 413 xfunc.Func.Enter = body 414 xfunc.Func.Needctxt = nvar > 0 415 } 416 417 lineno = int32(lno) 418 } 419 420 func walkclosure(func_ *Node, init **NodeList) *Node { 421 // If no closure vars, don't bother wrapping. 422 if func_.Func.Cvars == nil { 423 return func_.Closure.Nname 424 } 425 426 // Create closure in the form of a composite literal. 427 // supposing the closure captures an int i and a string s 428 // and has one float64 argument and no results, 429 // the generated code looks like: 430 // 431 // clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s} 432 // 433 // The use of the struct provides type information to the garbage 434 // collector so that it can walk the closure. We could use (in this case) 435 // [3]unsafe.Pointer instead, but that would leave the gc in the dark. 436 // The information appears in the binary in the form of type descriptors; 437 // the struct is unnamed so that closures in multiple packages with the 438 // same struct type can share the descriptor. 439 440 typ := Nod(OTSTRUCT, nil, nil) 441 442 typ.List = list1(Nod(ODCLFIELD, newname(Lookup(".F")), typenod(Types[TUINTPTR]))) 443 var typ1 *Node 444 var v *Node 445 for l := func_.Func.Cvars; l != nil; l = l.Next { 446 v = l.N 447 if v.Op == OXXX { 448 continue 449 } 450 typ1 = typenod(v.Type) 451 if !v.Byval { 452 typ1 = Nod(OIND, typ1, nil) 453 } 454 typ.List = list(typ.List, Nod(ODCLFIELD, newname(v.Sym), typ1)) 455 } 456 457 clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil)) 458 clos.Esc = func_.Esc 459 clos.Right.Implicit = true 460 clos.List = concat(list1(Nod(OCFUNC, func_.Closure.Nname, nil)), func_.Func.Enter) 461 462 // Force type conversion from *struct to the func type. 463 clos = Nod(OCONVNOP, clos, nil) 464 465 clos.Type = func_.Type 466 467 typecheck(&clos, Erv) 468 469 // typecheck will insert a PTRLIT node under CONVNOP, 470 // tag it with escape analysis result. 471 clos.Left.Esc = func_.Esc 472 473 // non-escaping temp to use, if any. 474 // orderexpr did not compute the type; fill it in now. 475 if func_.Alloc != nil { 476 func_.Alloc.Type = clos.Left.Left.Type 477 func_.Alloc.Orig.Type = func_.Alloc.Type 478 clos.Left.Right = func_.Alloc 479 func_.Alloc = nil 480 } 481 482 walkexpr(&clos, init) 483 484 return clos 485 } 486 487 func typecheckpartialcall(fn *Node, sym *Node) { 488 switch fn.Op { 489 case ODOTINTER, ODOTMETH: 490 break 491 492 default: 493 Fatal("invalid typecheckpartialcall") 494 } 495 496 // Create top-level function. 497 fn.Nname = makepartialcall(fn, fn.Type, sym) 498 499 fn.Right = sym 500 fn.Op = OCALLPART 501 fn.Type = fn.Nname.Type 502 } 503 504 var makepartialcall_gopkg *Pkg 505 506 func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node { 507 var p string 508 509 rcvrtype := fn.Left.Type 510 if exportname(meth.Sym.Name) { 511 p = fmt.Sprintf("(%v).%s-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), meth.Sym.Name) 512 } else { 513 p = fmt.Sprintf("(%v).(%v)-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), Sconv(meth.Sym, obj.FmtLeft)) 514 } 515 basetype := rcvrtype 516 if Isptr[rcvrtype.Etype] { 517 basetype = basetype.Type 518 } 519 if basetype.Etype != TINTER && basetype.Sym == nil { 520 Fatal("missing base type for %v", Tconv(rcvrtype, 0)) 521 } 522 523 var spkg *Pkg 524 if basetype.Sym != nil { 525 spkg = basetype.Sym.Pkg 526 } 527 if spkg == nil { 528 if makepartialcall_gopkg == nil { 529 makepartialcall_gopkg = mkpkg("go") 530 } 531 spkg = makepartialcall_gopkg 532 } 533 534 sym := Pkglookup(p, spkg) 535 536 if sym.Flags&SymUniq != 0 { 537 return sym.Def 538 } 539 sym.Flags |= SymUniq 540 541 savecurfn := Curfn 542 Curfn = nil 543 544 xtype := Nod(OTFUNC, nil, nil) 545 i := 0 546 var l *NodeList 547 var callargs *NodeList 548 ddd := false 549 xfunc := Nod(ODCLFUNC, nil, nil) 550 Curfn = xfunc 551 var fld *Node 552 var n *Node 553 for t := getinargx(t0).Type; t != nil; t = t.Down { 554 n = newname(Lookupf("a%d", i)) 555 i++ 556 n.Class = PPARAM 557 xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) 558 callargs = list(callargs, n) 559 fld = Nod(ODCLFIELD, n, typenod(t.Type)) 560 if t.Isddd { 561 fld.Isddd = true 562 ddd = true 563 } 564 565 l = list(l, fld) 566 } 567 568 xtype.List = l 569 i = 0 570 l = nil 571 var retargs *NodeList 572 for t := getoutargx(t0).Type; t != nil; t = t.Down { 573 n = newname(Lookupf("r%d", i)) 574 i++ 575 n.Class = PPARAMOUT 576 xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) 577 retargs = list(retargs, n) 578 l = list(l, Nod(ODCLFIELD, n, typenod(t.Type))) 579 } 580 581 xtype.Rlist = l 582 583 xfunc.Func.Dupok = true 584 xfunc.Nname = newfuncname(sym) 585 xfunc.Nname.Sym.Flags |= SymExported // disable export 586 xfunc.Nname.Ntype = xtype 587 xfunc.Nname.Defn = xfunc 588 declare(xfunc.Nname, PFUNC) 589 590 // Declare and initialize variable holding receiver. 591 592 xfunc.Func.Needctxt = true 593 cv := Nod(OCLOSUREVAR, nil, nil) 594 cv.Xoffset = int64(Widthptr) 595 cv.Type = rcvrtype 596 if int(cv.Type.Align) > Widthptr { 597 cv.Xoffset = int64(cv.Type.Align) 598 } 599 ptr := Nod(ONAME, nil, nil) 600 ptr.Sym = Lookup("rcvr") 601 ptr.Class = PAUTO 602 ptr.Addable = true 603 ptr.Ullman = 1 604 ptr.Used = true 605 ptr.Curfn = xfunc 606 xfunc.Func.Dcl = list(xfunc.Func.Dcl, ptr) 607 var body *NodeList 608 if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) { 609 ptr.Ntype = typenod(rcvrtype) 610 body = list(body, Nod(OAS, ptr, cv)) 611 } else { 612 ptr.Ntype = typenod(Ptrto(rcvrtype)) 613 body = list(body, Nod(OAS, ptr, Nod(OADDR, cv, nil))) 614 } 615 616 call := Nod(OCALL, Nod(OXDOT, ptr, meth), nil) 617 call.List = callargs 618 call.Isddd = ddd 619 if t0.Outtuple == 0 { 620 body = list(body, call) 621 } else { 622 n := Nod(OAS2, nil, nil) 623 n.List = retargs 624 n.Rlist = list1(call) 625 body = list(body, n) 626 n = Nod(ORETURN, nil, nil) 627 body = list(body, n) 628 } 629 630 xfunc.Nbody = body 631 632 typecheck(&xfunc, Etop) 633 sym.Def = xfunc 634 xtop = list(xtop, xfunc) 635 Curfn = savecurfn 636 637 return xfunc 638 } 639 640 func walkpartialcall(n *Node, init **NodeList) *Node { 641 // Create closure in the form of a composite literal. 642 // For x.M with receiver (x) type T, the generated code looks like: 643 // 644 // clos = &struct{F uintptr; R T}{M.T·f, x} 645 // 646 // Like walkclosure above. 647 648 if Isinter(n.Left.Type) { 649 // Trigger panic for method on nil interface now. 650 // Otherwise it happens in the wrapper and is confusing. 651 n.Left = cheapexpr(n.Left, init) 652 653 checknil(n.Left, init) 654 } 655 656 typ := Nod(OTSTRUCT, nil, nil) 657 typ.List = list1(Nod(ODCLFIELD, newname(Lookup("F")), typenod(Types[TUINTPTR]))) 658 typ.List = list(typ.List, Nod(ODCLFIELD, newname(Lookup("R")), typenod(n.Left.Type))) 659 660 clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil)) 661 clos.Esc = n.Esc 662 clos.Right.Implicit = true 663 clos.List = list1(Nod(OCFUNC, n.Nname.Nname, nil)) 664 clos.List = list(clos.List, n.Left) 665 666 // Force type conversion from *struct to the func type. 667 clos = Nod(OCONVNOP, clos, nil) 668 669 clos.Type = n.Type 670 671 typecheck(&clos, Erv) 672 673 // typecheck will insert a PTRLIT node under CONVNOP, 674 // tag it with escape analysis result. 675 clos.Left.Esc = n.Esc 676 677 // non-escaping temp to use, if any. 678 // orderexpr did not compute the type; fill it in now. 679 if n.Alloc != nil { 680 n.Alloc.Type = clos.Left.Left.Type 681 n.Alloc.Orig.Type = n.Alloc.Type 682 clos.Left.Right = n.Alloc 683 n.Alloc = nil 684 } 685 686 walkexpr(&clos, init) 687 688 return clos 689 }