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