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