github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/cmd/internal/gc/esc.go (about) 1 // Copyright 2011 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 "strings" 11 ) 12 13 // Run analysis on minimal sets of mutually recursive functions 14 // or single non-recursive functions, bottom up. 15 // 16 // Finding these sets is finding strongly connected components 17 // in the static call graph. The algorithm for doing that is taken 18 // from Sedgewick, Algorithms, Second Edition, p. 482, with two 19 // adaptations. 20 // 21 // First, a hidden closure function (n->curfn != N) cannot be the 22 // root of a connected component. Refusing to use it as a root 23 // forces it into the component of the function in which it appears. 24 // This is more convenient for escape analysis. 25 // 26 // Second, each function becomes two virtual nodes in the graph, 27 // with numbers n and n+1. We record the function's node number as n 28 // but search from node n+1. If the search tells us that the component 29 // number (min) is n+1, we know that this is a trivial component: one function 30 // plus its closures. If the search tells us that the component number is 31 // n, then there was a path from node n+1 back to node n, meaning that 32 // the function set is mutually recursive. The escape analysis can be 33 // more precise when analyzing a single non-recursive function than 34 // when analyzing a set of mutually recursive functions. 35 36 // TODO(rsc): Look into using a map[*Node]bool instead of walkgen, 37 // to allow analysis passes to use walkgen themselves. 38 39 type bottomUpVisitor struct { 40 analyze func(*NodeList, bool) 41 visitgen uint32 42 stack *NodeList 43 } 44 45 // visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list. 46 // It calls analyze with successive groups of functions, working from 47 // the bottom of the call graph upward. Each time analyze is called with 48 // a list of functions, every function on that list only calls other functions 49 // on the list or functions that have been passed in previous invocations of 50 // analyze. Closures appear in the same list as their outer functions. 51 // The lists are as short as possible while preserving those requirements. 52 // (In a typical program, many invocations of analyze will be passed just 53 // a single function.) The boolean argument 'recursive' passed to analyze 54 // specifies whether the functions on the list are mutually recursive. 55 // If recursive is false, the list consists of only a single function and its closures. 56 // If recursive is true, the list may still contain only a single function, 57 // if that function is itself recursive. 58 func visitBottomUp(list *NodeList, analyze func(list *NodeList, recursive bool)) { 59 for l := list; l != nil; l = l.Next { 60 l.N.Walkgen = 0 61 } 62 63 var v bottomUpVisitor 64 v.analyze = analyze 65 for l := list; l != nil; l = l.Next { 66 if l.N.Op == ODCLFUNC && l.N.Curfn == nil { 67 v.visit(l.N) 68 } 69 } 70 71 for l := list; l != nil; l = l.Next { 72 l.N.Walkgen = 0 73 } 74 } 75 76 func (v *bottomUpVisitor) visit(n *Node) uint32 { 77 if n.Walkgen > 0 { 78 // already visited 79 return n.Walkgen 80 } 81 82 v.visitgen++ 83 n.Walkgen = v.visitgen 84 v.visitgen++ 85 min := v.visitgen 86 87 l := new(NodeList) 88 l.Next = v.stack 89 l.N = n 90 v.stack = l 91 min = v.visitcodelist(n.Nbody, min) 92 if (min == n.Walkgen || min == n.Walkgen+1) && n.Curfn == nil { 93 // This node is the root of a strongly connected component. 94 95 // The original min passed to visitcodelist was n->walkgen+1. 96 // If visitcodelist found its way back to n->walkgen, then this 97 // block is a set of mutually recursive functions. 98 // Otherwise it's just a lone function that does not recurse. 99 recursive := min == n.Walkgen 100 101 // Remove connected component from stack. 102 // Mark walkgen so that future visits return a large number 103 // so as not to affect the caller's min. 104 block := v.stack 105 106 var l *NodeList 107 for l = v.stack; l.N != n; l = l.Next { 108 l.N.Walkgen = ^uint32(0) 109 } 110 n.Walkgen = ^uint32(0) 111 v.stack = l.Next 112 l.Next = nil 113 114 // Run escape analysis on this set of functions. 115 v.analyze(block, recursive) 116 } 117 118 return min 119 } 120 121 func (v *bottomUpVisitor) visitcodelist(l *NodeList, min uint32) uint32 { 122 for ; l != nil; l = l.Next { 123 min = v.visitcode(l.N, min) 124 } 125 return min 126 } 127 128 func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 { 129 if n == nil { 130 return min 131 } 132 133 min = v.visitcodelist(n.Ninit, min) 134 min = v.visitcode(n.Left, min) 135 min = v.visitcode(n.Right, min) 136 min = v.visitcodelist(n.List, min) 137 min = v.visitcode(n.Ntest, min) 138 min = v.visitcode(n.Nincr, min) 139 min = v.visitcodelist(n.Nbody, min) 140 min = v.visitcodelist(n.Nelse, min) 141 min = v.visitcodelist(n.Rlist, min) 142 143 if n.Op == OCALLFUNC || n.Op == OCALLMETH { 144 fn := n.Left 145 if n.Op == OCALLMETH { 146 fn = n.Left.Right.Sym.Def 147 } 148 if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Defn != nil { 149 m := v.visit(fn.Defn) 150 if m < min { 151 min = m 152 } 153 } 154 } 155 156 if n.Op == OCLOSURE { 157 m := v.visit(n.Closure) 158 if m < min { 159 min = m 160 } 161 } 162 163 return min 164 } 165 166 // Escape analysis. 167 168 // An escape analysis pass for a set of functions. 169 // The analysis assumes that closures and the functions in which they 170 // appear are analyzed together, so that the aliasing between their 171 // variables can be modeled more precisely. 172 // 173 // First escfunc, esc and escassign recurse over the ast of each 174 // function to dig out flow(dst,src) edges between any 175 // pointer-containing nodes and store them in dst->escflowsrc. For 176 // variables assigned to a variable in an outer scope or used as a 177 // return value, they store a flow(theSink, src) edge to a fake node 178 // 'the Sink'. For variables referenced in closures, an edge 179 // flow(closure, &var) is recorded and the flow of a closure itself to 180 // an outer scope is tracked the same way as other variables. 181 // 182 // Then escflood walks the graph starting at theSink and tags all 183 // variables of it can reach an & node as escaping and all function 184 // parameters it can reach as leaking. 185 // 186 // If a value's address is taken but the address does not escape, 187 // then the value can stay on the stack. If the value new(T) does 188 // not escape, then new(T) can be rewritten into a stack allocation. 189 // The same is true of slice literals. 190 // 191 // If optimizations are disabled (-N), this code is not used. 192 // Instead, the compiler assumes that any value whose address 193 // is taken without being immediately dereferenced 194 // needs to be moved to the heap, and new(T) and slice 195 // literals are always real allocations. 196 197 func escapes(all *NodeList) { 198 visitBottomUp(all, escAnalyze) 199 } 200 201 const ( 202 EscFuncUnknown = 0 + iota 203 EscFuncPlanned 204 EscFuncStarted 205 EscFuncTagged 206 ) 207 208 type EscState struct { 209 // Fake node that all 210 // - return values and output variables 211 // - parameters on imported functions not marked 'safe' 212 // - assignments to global variables 213 // flow to. 214 theSink Node 215 216 // If an analyzed function is recorded to return 217 // pieces obtained via indirection from a parameter, 218 // and later there is a call f(x) to that function, 219 // we create a link funcParam <- x to record that fact. 220 // The funcParam node is handled specially in escflood. 221 funcParam Node 222 223 dsts *NodeList // all dst nodes 224 loopdepth int // for detecting nested loop scopes 225 pdepth int // for debug printing in recursions. 226 dstcount int // diagnostic 227 edgecount int // diagnostic 228 noesc *NodeList // list of possible non-escaping nodes, for printing 229 recursive bool // recursive function or group of mutually recursive functions. 230 } 231 232 var tags [16]*string 233 234 // mktag returns the string representation for an escape analysis tag. 235 func mktag(mask int) *string { 236 switch mask & EscMask { 237 case EscNone, EscReturn: 238 break 239 240 default: 241 Fatal("escape mktag") 242 } 243 244 mask >>= EscBits 245 246 if mask < len(tags) && tags[mask] != nil { 247 return tags[mask] 248 } 249 250 s := fmt.Sprintf("esc:0x%x", mask) 251 if mask < len(tags) { 252 tags[mask] = &s 253 } 254 return &s 255 } 256 257 func parsetag(note *string) int { 258 if note == nil || !strings.HasPrefix(*note, "esc:") { 259 return EscUnknown 260 } 261 em := atoi((*note)[4:]) 262 if em == 0 { 263 return EscNone 264 } 265 return EscReturn | em<<EscBits 266 } 267 268 func escAnalyze(all *NodeList, recursive bool) { 269 var es EscState 270 e := &es 271 e.theSink.Op = ONAME 272 e.theSink.Orig = &e.theSink 273 e.theSink.Class = PEXTERN 274 e.theSink.Sym = Lookup(".sink") 275 e.theSink.Escloopdepth = -1 276 e.recursive = recursive 277 278 e.funcParam.Op = ONAME 279 e.funcParam.Orig = &e.funcParam 280 e.funcParam.Class = PAUTO 281 e.funcParam.Sym = Lookup(".param") 282 e.funcParam.Escloopdepth = 10000000 283 284 for l := all; l != nil; l = l.Next { 285 if l.N.Op == ODCLFUNC { 286 l.N.Esc = EscFuncPlanned 287 } 288 } 289 290 // flow-analyze functions 291 for l := all; l != nil; l = l.Next { 292 if l.N.Op == ODCLFUNC { 293 escfunc(e, l.N) 294 } 295 } 296 297 // print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount); 298 299 // visit the upstream of each dst, mark address nodes with 300 // addrescapes, mark parameters unsafe 301 for l := e.dsts; l != nil; l = l.Next { 302 escflood(e, l.N) 303 } 304 305 // for all top level functions, tag the typenodes corresponding to the param nodes 306 for l := all; l != nil; l = l.Next { 307 if l.N.Op == ODCLFUNC { 308 esctag(e, l.N) 309 } 310 } 311 312 if Debug['m'] != 0 { 313 for l := e.noesc; l != nil; l = l.Next { 314 if l.N.Esc == EscNone { 315 var tmp *Sym 316 if l.N.Curfn != nil && l.N.Curfn.Nname != nil { 317 tmp = l.N.Curfn.Nname.Sym 318 } else { 319 tmp = nil 320 } 321 Warnl(int(l.N.Lineno), "%v %v does not escape", Sconv(tmp, 0), Nconv(l.N, obj.FmtShort)) 322 } 323 } 324 } 325 } 326 327 func escfunc(e *EscState, func_ *Node) { 328 // print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":""); 329 330 if func_.Esc != 1 { 331 Fatal("repeat escfunc %v", Nconv(func_.Nname, 0)) 332 } 333 func_.Esc = EscFuncStarted 334 335 saveld := e.loopdepth 336 e.loopdepth = 1 337 savefn := Curfn 338 Curfn = func_ 339 340 for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { 341 if ll.N.Op != ONAME { 342 continue 343 } 344 switch ll.N.Class { 345 // out params are in a loopdepth between the sink and all local variables 346 case PPARAMOUT: 347 ll.N.Escloopdepth = 0 348 349 case PPARAM: 350 ll.N.Escloopdepth = 1 351 if ll.N.Type != nil && !haspointers(ll.N.Type) { 352 break 353 } 354 if Curfn.Nbody == nil && !Curfn.Noescape { 355 ll.N.Esc = EscHeap 356 } else { 357 ll.N.Esc = EscNone // prime for escflood later 358 } 359 e.noesc = list(e.noesc, ll.N) 360 } 361 } 362 363 // in a mutually recursive group we lose track of the return values 364 if e.recursive { 365 for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { 366 if ll.N.Op == ONAME && ll.N.Class == PPARAMOUT { 367 escflows(e, &e.theSink, ll.N) 368 } 369 } 370 } 371 372 escloopdepthlist(e, Curfn.Nbody) 373 esclist(e, Curfn.Nbody, Curfn) 374 Curfn = savefn 375 e.loopdepth = saveld 376 } 377 378 // Mark labels that have no backjumps to them as not increasing e->loopdepth. 379 // Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat 380 // and set it to one of the following two. Then in esc we'll clear it again. 381 var looping Label 382 383 var nonlooping Label 384 385 func escloopdepthlist(e *EscState, l *NodeList) { 386 for ; l != nil; l = l.Next { 387 escloopdepth(e, l.N) 388 } 389 } 390 391 func escloopdepth(e *EscState, n *Node) { 392 if n == nil { 393 return 394 } 395 396 escloopdepthlist(e, n.Ninit) 397 398 switch n.Op { 399 case OLABEL: 400 if n.Left == nil || n.Left.Sym == nil { 401 Fatal("esc:label without label: %v", Nconv(n, obj.FmtSign)) 402 } 403 404 // Walk will complain about this label being already defined, but that's not until 405 // after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc 406 // if(n->left->sym->label != nil) 407 // fatal("escape analysis messed up analyzing label: %+N", n); 408 n.Left.Sym.Label = &nonlooping 409 410 case OGOTO: 411 if n.Left == nil || n.Left.Sym == nil { 412 Fatal("esc:goto without label: %v", Nconv(n, obj.FmtSign)) 413 } 414 415 // If we come past one that's uninitialized, this must be a (harmless) forward jump 416 // but if it's set to nonlooping the label must have preceded this goto. 417 if n.Left.Sym.Label == &nonlooping { 418 n.Left.Sym.Label = &looping 419 } 420 } 421 422 escloopdepth(e, n.Left) 423 escloopdepth(e, n.Right) 424 escloopdepthlist(e, n.List) 425 escloopdepth(e, n.Ntest) 426 escloopdepth(e, n.Nincr) 427 escloopdepthlist(e, n.Nbody) 428 escloopdepthlist(e, n.Nelse) 429 escloopdepthlist(e, n.Rlist) 430 } 431 432 func esclist(e *EscState, l *NodeList, up *Node) { 433 for ; l != nil; l = l.Next { 434 esc(e, l.N, up) 435 } 436 } 437 438 func esc(e *EscState, n *Node, up *Node) { 439 if n == nil { 440 return 441 } 442 443 lno := int(setlineno(n)) 444 445 // ninit logically runs at a different loopdepth than the rest of the for loop. 446 esclist(e, n.Ninit, n) 447 448 if n.Op == OFOR || n.Op == ORANGE { 449 e.loopdepth++ 450 } 451 452 // type switch variables have no ODCL. 453 // process type switch as declaration. 454 // must happen before processing of switch body, 455 // so before recursion. 456 if n.Op == OSWITCH && n.Ntest != nil && n.Ntest.Op == OTYPESW { 457 for ll := n.List; ll != nil; ll = ll.Next { // cases 458 459 // ll->n->nname is the variable per case 460 if ll.N.Nname != nil { 461 ll.N.Nname.Escloopdepth = e.loopdepth 462 } 463 } 464 } 465 466 esc(e, n.Left, n) 467 esc(e, n.Right, n) 468 esc(e, n.Ntest, n) 469 esc(e, n.Nincr, n) 470 esclist(e, n.Nbody, n) 471 esclist(e, n.Nelse, n) 472 esclist(e, n.List, n) 473 esclist(e, n.Rlist, n) 474 475 if n.Op == OFOR || n.Op == ORANGE { 476 e.loopdepth-- 477 } 478 479 if Debug['m'] > 1 { 480 var tmp *Sym 481 if Curfn != nil && Curfn.Nname != nil { 482 tmp = Curfn.Nname.Sym 483 } else { 484 tmp = nil 485 } 486 fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, Sconv(tmp, 0), Nconv(n, 0)) 487 } 488 489 switch n.Op { 490 // Record loop depth at declaration. 491 case ODCL: 492 if n.Left != nil { 493 n.Left.Escloopdepth = e.loopdepth 494 } 495 496 case OLABEL: 497 if n.Left.Sym.Label == &nonlooping { 498 if Debug['m'] > 1 { 499 fmt.Printf("%v:%v non-looping label\n", Ctxt.Line(int(lineno)), Nconv(n, 0)) 500 } 501 } else if n.Left.Sym.Label == &looping { 502 if Debug['m'] > 1 { 503 fmt.Printf("%v: %v looping label\n", Ctxt.Line(int(lineno)), Nconv(n, 0)) 504 } 505 e.loopdepth++ 506 } 507 508 // See case OLABEL in escloopdepth above 509 // else if(n->left->sym->label == nil) 510 // fatal("escape analysis missed or messed up a label: %+N", n); 511 512 n.Left.Sym.Label = nil 513 514 // Everything but fixed array is a dereference. 515 case ORANGE: 516 if Isfixedarray(n.Type) && n.List != nil && n.List.Next != nil { 517 escassign(e, n.List.Next.N, n.Right) 518 } 519 520 case OSWITCH: 521 if n.Ntest != nil && n.Ntest.Op == OTYPESW { 522 for ll := n.List; ll != nil; ll = ll.Next { // cases 523 524 // ntest->right is the argument of the .(type), 525 // ll->n->nname is the variable per case 526 escassign(e, ll.N.Nname, n.Ntest.Right) 527 } 528 } 529 530 // Filter out the following special case. 531 // 532 // func (b *Buffer) Foo() { 533 // n, m := ... 534 // b.buf = b.buf[n:m] 535 // } 536 // 537 // This assignment is a no-op for escape analysis, 538 // it does not store any new pointers into b that were not already there. 539 // However, without this special case b will escape, because we assign to OIND/ODOTPTR. 540 case OAS, OASOP: 541 if (n.Left.Op == OIND || n.Left.Op == ODOTPTR) && n.Left.Left.Op == ONAME && // dst is ONAME dereference 542 (n.Right.Op == OSLICE || n.Right.Op == OSLICE3 || n.Right.Op == OSLICESTR) && // src is slice operation 543 (n.Right.Left.Op == OIND || n.Right.Left.Op == ODOTPTR) && n.Right.Left.Left.Op == ONAME && // slice is applied to ONAME dereference 544 n.Left.Left == n.Right.Left.Left { // dst and src reference the same base ONAME 545 546 // Here we also assume that the statement will not contain calls, 547 // that is, that order will move any calls to init. 548 // Otherwise base ONAME value could change between the moments 549 // when we evaluate it for dst and for src. 550 // 551 // Note, this optimization does not apply to OSLICEARR, 552 // because it does introduce a new pointer into b that was not already there 553 // (pointer to b itself). After such assignment, if b contents escape, 554 // b escapes as well. If we ignore such OSLICEARR, we will conclude 555 // that b does not escape when b contents do. 556 if Debug['m'] != 0 { 557 var tmp *Sym 558 if n.Curfn != nil && n.Curfn.Nname != nil { 559 tmp = n.Curfn.Nname.Sym 560 } else { 561 tmp = nil 562 } 563 Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", Sconv(tmp, 0), Nconv(n.Left, obj.FmtShort)) 564 } 565 566 break 567 } 568 569 escassign(e, n.Left, n.Right) 570 571 case OAS2: // x,y = a,b 572 if count(n.List) == count(n.Rlist) { 573 ll := n.List 574 lr := n.Rlist 575 for ; ll != nil; ll, lr = ll.Next, lr.Next { 576 escassign(e, ll.N, lr.N) 577 } 578 } 579 580 case OAS2RECV, // v, ok = <-ch 581 OAS2MAPR, // v, ok = m[k] 582 OAS2DOTTYPE: // v, ok = x.(type) 583 escassign(e, n.List.N, n.Rlist.N) 584 585 case OSEND: // ch <- x 586 escassign(e, &e.theSink, n.Right) 587 588 case ODEFER: 589 if e.loopdepth == 1 { // top level 590 break 591 } 592 // arguments leak out of scope 593 // TODO: leak to a dummy node instead 594 fallthrough 595 596 case OPROC: 597 // go f(x) - f and x escape 598 escassign(e, &e.theSink, n.Left.Left) 599 600 escassign(e, &e.theSink, n.Left.Right) // ODDDARG for call 601 for ll := n.Left.List; ll != nil; ll = ll.Next { 602 escassign(e, &e.theSink, ll.N) 603 } 604 605 case OCALLMETH, OCALLFUNC, OCALLINTER: 606 esccall(e, n, up) 607 608 // esccall already done on n->rlist->n. tie it's escretval to n->list 609 case OAS2FUNC: // x,y = f() 610 lr := n.Rlist.N.Escretval 611 612 var ll *NodeList 613 for ll = n.List; lr != nil && ll != nil; lr, ll = lr.Next, ll.Next { 614 escassign(e, ll.N, lr.N) 615 } 616 if lr != nil || ll != nil { 617 Fatal("esc oas2func") 618 } 619 620 case ORETURN: 621 ll := n.List 622 if count(n.List) == 1 && Curfn.Type.Outtuple > 1 { 623 // OAS2FUNC in disguise 624 // esccall already done on n->list->n 625 // tie n->list->n->escretval to curfn->dcl PPARAMOUT's 626 ll = n.List.N.Escretval 627 } 628 629 for lr := Curfn.Func.Dcl; lr != nil && ll != nil; lr = lr.Next { 630 if lr.N.Op != ONAME || lr.N.Class != PPARAMOUT { 631 continue 632 } 633 escassign(e, lr.N, ll.N) 634 ll = ll.Next 635 } 636 637 if ll != nil { 638 Fatal("esc return list") 639 } 640 641 // Argument could leak through recover. 642 case OPANIC: 643 escassign(e, &e.theSink, n.Left) 644 645 case OAPPEND: 646 if !n.Isddd { 647 for ll := n.List.Next; ll != nil; ll = ll.Next { 648 escassign(e, &e.theSink, ll.N) // lose track of assign to dereference 649 } 650 } 651 652 case OCONV, OCONVNOP: 653 escassign(e, n, n.Left) 654 655 case OCONVIFACE: 656 n.Esc = EscNone // until proven otherwise 657 e.noesc = list(e.noesc, n) 658 n.Escloopdepth = e.loopdepth 659 escassign(e, n, n.Left) 660 661 case OARRAYLIT: 662 if Isslice(n.Type) { 663 n.Esc = EscNone // until proven otherwise 664 e.noesc = list(e.noesc, n) 665 n.Escloopdepth = e.loopdepth 666 667 // Values make it to memory, lose track. 668 for ll := n.List; ll != nil; ll = ll.Next { 669 escassign(e, &e.theSink, ll.N.Right) 670 } 671 } else { 672 // Link values to array. 673 for ll := n.List; ll != nil; ll = ll.Next { 674 escassign(e, n, ll.N.Right) 675 } 676 } 677 678 // Link values to struct. 679 case OSTRUCTLIT: 680 for ll := n.List; ll != nil; ll = ll.Next { 681 escassign(e, n, ll.N.Right) 682 } 683 684 case OPTRLIT: 685 n.Esc = EscNone // until proven otherwise 686 e.noesc = list(e.noesc, n) 687 n.Escloopdepth = e.loopdepth 688 689 // Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too. 690 escassign(e, n, n.Left) 691 692 case OCALLPART: 693 n.Esc = EscNone // until proven otherwise 694 e.noesc = list(e.noesc, n) 695 n.Escloopdepth = e.loopdepth 696 697 // Contents make it to memory, lose track. 698 escassign(e, &e.theSink, n.Left) 699 700 case OMAPLIT: 701 n.Esc = EscNone // until proven otherwise 702 e.noesc = list(e.noesc, n) 703 n.Escloopdepth = e.loopdepth 704 705 // Keys and values make it to memory, lose track. 706 for ll := n.List; ll != nil; ll = ll.Next { 707 escassign(e, &e.theSink, ll.N.Left) 708 escassign(e, &e.theSink, ll.N.Right) 709 } 710 711 // Link addresses of captured variables to closure. 712 case OCLOSURE: 713 var a *Node 714 var v *Node 715 for ll := n.Func.Cvars; ll != nil; ll = ll.Next { 716 v = ll.N 717 if v.Op == OXXX { // unnamed out argument; see dcl.c:/^funcargs 718 continue 719 } 720 a = v.Closure 721 if !v.Byval { 722 a = Nod(OADDR, a, nil) 723 a.Lineno = v.Lineno 724 a.Escloopdepth = e.loopdepth 725 typecheck(&a, Erv) 726 } 727 728 escassign(e, n, a) 729 } 730 fallthrough 731 732 // fallthrough 733 case OMAKECHAN, 734 OMAKEMAP, 735 OMAKESLICE, 736 ONEW, 737 OARRAYRUNESTR, 738 OARRAYBYTESTR, 739 OSTRARRAYRUNE, 740 OSTRARRAYBYTE, 741 ORUNESTR: 742 n.Escloopdepth = e.loopdepth 743 744 n.Esc = EscNone // until proven otherwise 745 e.noesc = list(e.noesc, n) 746 747 case OADDSTR: 748 n.Escloopdepth = e.loopdepth 749 n.Esc = EscNone // until proven otherwise 750 e.noesc = list(e.noesc, n) 751 752 // Arguments of OADDSTR do not escape. 753 754 case OADDR: 755 n.Esc = EscNone // until proven otherwise 756 e.noesc = list(e.noesc, n) 757 758 // current loop depth is an upper bound on actual loop depth 759 // of addressed value. 760 n.Escloopdepth = e.loopdepth 761 762 // for &x, use loop depth of x if known. 763 // it should always be known, but if not, be conservative 764 // and keep the current loop depth. 765 if n.Left.Op == ONAME { 766 switch n.Left.Class { 767 case PAUTO: 768 if n.Left.Escloopdepth != 0 { 769 n.Escloopdepth = n.Left.Escloopdepth 770 } 771 772 // PPARAM is loop depth 1 always. 773 // PPARAMOUT is loop depth 0 for writes 774 // but considered loop depth 1 for address-of, 775 // so that writing the address of one result 776 // to another (or the same) result makes the 777 // first result move to the heap. 778 case PPARAM, PPARAMOUT: 779 n.Escloopdepth = 1 780 } 781 } 782 } 783 784 lineno = int32(lno) 785 } 786 787 // Assert that expr somehow gets assigned to dst, if non nil. for 788 // dst==nil, any name node expr still must be marked as being 789 // evaluated in curfn. For expr==nil, dst must still be examined for 790 // evaluations inside it (e.g *f(x) = y) 791 func escassign(e *EscState, dst *Node, src *Node) { 792 if isblank(dst) || dst == nil || src == nil || src.Op == ONONAME || src.Op == OXXX { 793 return 794 } 795 796 if Debug['m'] > 1 { 797 var tmp *Sym 798 if Curfn != nil && Curfn.Nname != nil { 799 tmp = Curfn.Nname.Sym 800 } else { 801 tmp = nil 802 } 803 fmt.Printf("%v:[%d] %v escassign: %v(%v) = %v(%v)\n", Ctxt.Line(int(lineno)), e.loopdepth, Sconv(tmp, 0), Nconv(dst, obj.FmtShort), Jconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort)) 804 } 805 806 setlineno(dst) 807 808 // Analyze lhs of assignment. 809 // Replace dst with e->theSink if we can't track it. 810 switch dst.Op { 811 default: 812 Dump("dst", dst) 813 Fatal("escassign: unexpected dst") 814 815 case OARRAYLIT, 816 OCLOSURE, 817 OCONV, 818 OCONVIFACE, 819 OCONVNOP, 820 OMAPLIT, 821 OSTRUCTLIT, 822 OPTRLIT, 823 OCALLPART: 824 break 825 826 case ONAME: 827 if dst.Class == PEXTERN { 828 dst = &e.theSink 829 } 830 831 case ODOT: // treat "dst.x = src" as "dst = src" 832 escassign(e, dst.Left, src) 833 834 return 835 836 case OINDEX: 837 if Isfixedarray(dst.Left.Type) { 838 escassign(e, dst.Left, src) 839 return 840 } 841 842 dst = &e.theSink // lose track of dereference 843 844 case OIND, ODOTPTR: 845 dst = &e.theSink // lose track of dereference 846 847 // lose track of key and value 848 case OINDEXMAP: 849 escassign(e, &e.theSink, dst.Right) 850 851 dst = &e.theSink 852 } 853 854 lno := int(setlineno(src)) 855 e.pdepth++ 856 857 switch src.Op { 858 case OADDR, // dst = &x 859 OIND, // dst = *x 860 ODOTPTR, // dst = (*x).f 861 ONAME, 862 OPARAM, 863 ODDDARG, 864 OPTRLIT, 865 OARRAYLIT, 866 OMAPLIT, 867 OSTRUCTLIT, 868 OMAKECHAN, 869 OMAKEMAP, 870 OMAKESLICE, 871 OARRAYRUNESTR, 872 OARRAYBYTESTR, 873 OSTRARRAYRUNE, 874 OSTRARRAYBYTE, 875 OADDSTR, 876 ONEW, 877 OCALLPART, 878 ORUNESTR, 879 OCONVIFACE: 880 escflows(e, dst, src) 881 882 case OCLOSURE: 883 // OCLOSURE is lowered to OPTRLIT, 884 // insert OADDR to account for the additional indirection. 885 a := Nod(OADDR, src, nil) 886 a.Lineno = src.Lineno 887 a.Escloopdepth = src.Escloopdepth 888 a.Type = Ptrto(src.Type) 889 escflows(e, dst, a) 890 891 // Flowing multiple returns to a single dst happens when 892 // analyzing "go f(g())": here g() flows to sink (issue 4529). 893 case OCALLMETH, OCALLFUNC, OCALLINTER: 894 for ll := src.Escretval; ll != nil; ll = ll.Next { 895 escflows(e, dst, ll.N) 896 } 897 898 // A non-pointer escaping from a struct does not concern us. 899 case ODOT: 900 if src.Type != nil && !haspointers(src.Type) { 901 break 902 } 903 fallthrough 904 905 // Conversions, field access, slice all preserve the input value. 906 // fallthrough 907 case OCONV, 908 OCONVNOP, 909 ODOTMETH, 910 // treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC 911 // iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here 912 ODOTTYPE, 913 ODOTTYPE2, 914 OSLICE, 915 OSLICE3, 916 OSLICEARR, 917 OSLICE3ARR, 918 OSLICESTR: 919 // Conversions, field access, slice all preserve the input value. 920 escassign(e, dst, src.Left) 921 922 case OAPPEND: 923 // Append returns first argument. 924 escassign(e, dst, src.List.N) 925 926 case OINDEX: 927 // Index of array preserves input value. 928 if Isfixedarray(src.Left.Type) { 929 escassign(e, dst, src.Left) 930 } 931 932 // Might be pointer arithmetic, in which case 933 // the operands flow into the result. 934 // TODO(rsc): Decide what the story is here. This is unsettling. 935 case OADD, 936 OSUB, 937 OOR, 938 OXOR, 939 OMUL, 940 ODIV, 941 OMOD, 942 OLSH, 943 ORSH, 944 OAND, 945 OANDNOT, 946 OPLUS, 947 OMINUS, 948 OCOM: 949 escassign(e, dst, src.Left) 950 951 escassign(e, dst, src.Right) 952 } 953 954 e.pdepth-- 955 lineno = int32(lno) 956 } 957 958 func escassignfromtag(e *EscState, note *string, dsts *NodeList, src *Node) int { 959 em := parsetag(note) 960 961 if em == EscUnknown { 962 escassign(e, &e.theSink, src) 963 return em 964 } 965 966 if em == EscNone { 967 return em 968 } 969 970 // If content inside parameter (reached via indirection) 971 // escapes back to results, mark as such. 972 if em&EscContentEscapes != 0 { 973 escassign(e, &e.funcParam, src) 974 } 975 976 em0 := em 977 for em >>= EscReturnBits; em != 0 && dsts != nil; em, dsts = em>>1, dsts.Next { 978 if em&1 != 0 { 979 escassign(e, dsts.N, src) 980 } 981 } 982 983 if em != 0 && dsts == nil { 984 Fatal("corrupt esc tag %q or messed up escretval list\n", note) 985 } 986 return em0 987 } 988 989 // This is a bit messier than fortunate, pulled out of esc's big 990 // switch for clarity. We either have the paramnodes, which may be 991 // connected to other things through flows or we have the parameter type 992 // nodes, which may be marked "noescape". Navigating the ast is slightly 993 // different for methods vs plain functions and for imported vs 994 // this-package 995 func esccall(e *EscState, n *Node, up *Node) { 996 var fntype *Type 997 998 var fn *Node 999 switch n.Op { 1000 default: 1001 Fatal("esccall") 1002 1003 case OCALLFUNC: 1004 fn = n.Left 1005 fntype = fn.Type 1006 1007 case OCALLMETH: 1008 fn = n.Left.Right.Sym.Def 1009 if fn != nil { 1010 fntype = fn.Type 1011 } else { 1012 fntype = n.Left.Type 1013 } 1014 1015 case OCALLINTER: 1016 fntype = n.Left.Type 1017 } 1018 1019 ll := n.List 1020 if n.List != nil && n.List.Next == nil { 1021 a := n.List.N 1022 if a.Type.Etype == TSTRUCT && a.Type.Funarg != 0 { // f(g()). 1023 ll = a.Escretval 1024 } 1025 } 1026 1027 if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Defn != nil && fn.Defn.Nbody != nil && fn.Ntype != nil && fn.Defn.Esc < EscFuncTagged { 1028 // function in same mutually recursive group. Incorporate into flow graph. 1029 // print("esc local fn: %N\n", fn->ntype); 1030 if fn.Defn.Esc == EscFuncUnknown || n.Escretval != nil { 1031 Fatal("graph inconsistency") 1032 } 1033 1034 // set up out list on this call node 1035 for lr := fn.Ntype.Rlist; lr != nil; lr = lr.Next { 1036 n.Escretval = list(n.Escretval, lr.N.Left) // type.rlist -> dclfield -> ONAME (PPARAMOUT) 1037 } 1038 1039 // Receiver. 1040 if n.Op != OCALLFUNC { 1041 escassign(e, fn.Ntype.Left.Left, n.Left.Left) 1042 } 1043 1044 var src *Node 1045 for lr := fn.Ntype.List; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next { 1046 src = ll.N 1047 if lr.N.Isddd && !n.Isddd { 1048 // Introduce ODDDARG node to represent ... allocation. 1049 src = Nod(ODDDARG, nil, nil) 1050 1051 src.Type = typ(TARRAY) 1052 src.Type.Type = lr.N.Type.Type 1053 src.Type.Bound = int64(count(ll)) 1054 src.Type = Ptrto(src.Type) // make pointer so it will be tracked 1055 src.Escloopdepth = e.loopdepth 1056 src.Lineno = n.Lineno 1057 src.Esc = EscNone // until we find otherwise 1058 e.noesc = list(e.noesc, src) 1059 n.Right = src 1060 } 1061 1062 if lr.N.Left != nil { 1063 escassign(e, lr.N.Left, src) 1064 } 1065 if src != ll.N { 1066 break 1067 } 1068 } 1069 1070 // "..." arguments are untracked 1071 for ; ll != nil; ll = ll.Next { 1072 escassign(e, &e.theSink, ll.N) 1073 } 1074 1075 return 1076 } 1077 1078 // Imported or completely analyzed function. Use the escape tags. 1079 if n.Escretval != nil { 1080 Fatal("esc already decorated call %v\n", Nconv(n, obj.FmtSign)) 1081 } 1082 1083 // set up out list on this call node with dummy auto ONAMES in the current (calling) function. 1084 i := 0 1085 1086 var src *Node 1087 var buf string 1088 for t := getoutargx(fntype).Type; t != nil; t = t.Down { 1089 src = Nod(ONAME, nil, nil) 1090 buf = fmt.Sprintf(".dum%d", i) 1091 i++ 1092 src.Sym = Lookup(buf) 1093 src.Type = t.Type 1094 src.Class = PAUTO 1095 src.Curfn = Curfn 1096 src.Escloopdepth = e.loopdepth 1097 src.Used = true 1098 src.Lineno = n.Lineno 1099 n.Escretval = list(n.Escretval, src) 1100 } 1101 1102 // print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval); 1103 1104 // Receiver. 1105 if n.Op != OCALLFUNC { 1106 t := getthisx(fntype).Type 1107 src := n.Left.Left 1108 if haspointers(t.Type) { 1109 escassignfromtag(e, t.Note, n.Escretval, src) 1110 } 1111 } 1112 1113 var a *Node 1114 for t := getinargx(fntype).Type; ll != nil; ll = ll.Next { 1115 src = ll.N 1116 if t.Isddd && !n.Isddd { 1117 // Introduce ODDDARG node to represent ... allocation. 1118 src = Nod(ODDDARG, nil, nil) 1119 1120 src.Escloopdepth = e.loopdepth 1121 src.Lineno = n.Lineno 1122 src.Type = typ(TARRAY) 1123 src.Type.Type = t.Type.Type 1124 src.Type.Bound = int64(count(ll)) 1125 src.Type = Ptrto(src.Type) // make pointer so it will be tracked 1126 src.Esc = EscNone // until we find otherwise 1127 e.noesc = list(e.noesc, src) 1128 n.Right = src 1129 } 1130 1131 if haspointers(t.Type) { 1132 if escassignfromtag(e, t.Note, n.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC { 1133 a = src 1134 for a.Op == OCONVNOP { 1135 a = a.Left 1136 } 1137 switch a.Op { 1138 // The callee has already been analyzed, so its arguments have esc tags. 1139 // The argument is marked as not escaping at all. 1140 // Record that fact so that any temporary used for 1141 // synthesizing this expression can be reclaimed when 1142 // the function returns. 1143 // This 'noescape' is even stronger than the usual esc == EscNone. 1144 // src->esc == EscNone means that src does not escape the current function. 1145 // src->noescape = 1 here means that src does not escape this statement 1146 // in the current function. 1147 case OCALLPART, 1148 OCLOSURE, 1149 ODDDARG, 1150 OARRAYLIT, 1151 OPTRLIT, 1152 OSTRUCTLIT: 1153 a.Noescape = true 1154 } 1155 } 1156 } 1157 1158 if src != ll.N { 1159 break 1160 } 1161 t = t.Down 1162 } 1163 1164 // "..." arguments are untracked 1165 for ; ll != nil; ll = ll.Next { 1166 escassign(e, &e.theSink, ll.N) 1167 } 1168 } 1169 1170 // Store the link src->dst in dst, throwing out some quick wins. 1171 func escflows(e *EscState, dst *Node, src *Node) { 1172 if dst == nil || src == nil || dst == src { 1173 return 1174 } 1175 1176 // Don't bother building a graph for scalars. 1177 if src.Type != nil && !haspointers(src.Type) { 1178 return 1179 } 1180 1181 if Debug['m'] > 2 { 1182 fmt.Printf("%v::flows:: %v <- %v\n", Ctxt.Line(int(lineno)), Nconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort)) 1183 } 1184 1185 if dst.Escflowsrc == nil { 1186 e.dsts = list(e.dsts, dst) 1187 e.dstcount++ 1188 } 1189 1190 e.edgecount++ 1191 1192 dst.Escflowsrc = list(dst.Escflowsrc, src) 1193 } 1194 1195 // Whenever we hit a reference node, the level goes up by one, and whenever 1196 // we hit an OADDR, the level goes down by one. as long as we're on a level > 0 1197 // finding an OADDR just means we're following the upstream of a dereference, 1198 // so this address doesn't leak (yet). 1199 // If level == 0, it means the /value/ of this node can reach the root of this flood. 1200 // so if this node is an OADDR, it's argument should be marked as escaping iff 1201 // it's currfn/e->loopdepth are different from the flood's root. 1202 // Once an object has been moved to the heap, all of it's upstream should be considered 1203 // escaping to the global scope. 1204 func escflood(e *EscState, dst *Node) { 1205 switch dst.Op { 1206 case ONAME, OCLOSURE: 1207 break 1208 1209 default: 1210 return 1211 } 1212 1213 if Debug['m'] > 1 { 1214 var tmp *Sym 1215 if dst.Curfn != nil && dst.Curfn.Nname != nil { 1216 tmp = dst.Curfn.Nname.Sym 1217 } else { 1218 tmp = nil 1219 } 1220 fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", walkgen, Nconv(dst, obj.FmtShort), Sconv(tmp, 0), dst.Escloopdepth) 1221 } 1222 1223 for l := dst.Escflowsrc; l != nil; l = l.Next { 1224 walkgen++ 1225 escwalk(e, 0, dst, l.N) 1226 } 1227 } 1228 1229 // There appear to be some loops in the escape graph, causing 1230 // arbitrary recursion into deeper and deeper levels. 1231 // Cut this off safely by making minLevel sticky: once you 1232 // get that deep, you cannot go down any further but you also 1233 // cannot go up any further. This is a conservative fix. 1234 // Making minLevel smaller (more negative) would handle more 1235 // complex chains of indirections followed by address-of operations, 1236 // at the cost of repeating the traversal once for each additional 1237 // allowed level when a loop is encountered. Using -2 suffices to 1238 // pass all the tests we have written so far, which we assume matches 1239 // the level of complexity we want the escape analysis code to handle. 1240 const ( 1241 MinLevel = -2 1242 ) 1243 1244 func escwalk(e *EscState, level int, dst *Node, src *Node) { 1245 if src.Walkgen == walkgen && src.Esclevel <= int32(level) { 1246 return 1247 } 1248 src.Walkgen = walkgen 1249 src.Esclevel = int32(level) 1250 1251 if Debug['m'] > 1 { 1252 var tmp *Sym 1253 if src.Curfn != nil && src.Curfn.Nname != nil { 1254 tmp = src.Curfn.Nname.Sym 1255 } else { 1256 tmp = nil 1257 } 1258 fmt.Printf("escwalk: level:%d depth:%d %.*s %v(%v) scope:%v[%d]\n", level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), Sconv(tmp, 0), src.Escloopdepth) 1259 } 1260 1261 e.pdepth++ 1262 1263 // Input parameter flowing to output parameter? 1264 var leaks bool 1265 if dst.Op == ONAME && dst.Class == PPARAMOUT && dst.Vargen <= 20 { 1266 if src.Op == ONAME && src.Class == PPARAM && src.Curfn == dst.Curfn && src.Esc != EscScope && src.Esc != EscHeap { 1267 if level == 0 { 1268 if Debug['m'] != 0 { 1269 Warnl(int(src.Lineno), "leaking param: %v to result %v", Nconv(src, obj.FmtShort), Sconv(dst.Sym, 0)) 1270 } 1271 if src.Esc&EscMask != EscReturn { 1272 src.Esc = EscReturn 1273 } 1274 src.Esc |= 1 << uint((dst.Vargen-1)+EscReturnBits) 1275 goto recurse 1276 } else if level > 0 { 1277 if Debug['m'] != 0 { 1278 Warnl(int(src.Lineno), "%v leaking param %v content to result %v", Nconv(src.Curfn.Nname, 0), Nconv(src, obj.FmtShort), Sconv(dst.Sym, 0)) 1279 } 1280 if src.Esc&EscMask != EscReturn { 1281 src.Esc = EscReturn 1282 } 1283 src.Esc |= EscContentEscapes 1284 goto recurse 1285 } 1286 } 1287 } 1288 1289 // The second clause is for values pointed at by an object passed to a call 1290 // that returns something reached via indirect from the object. 1291 // We don't know which result it is or how many indirects, so we treat it as leaking. 1292 leaks = level <= 0 && dst.Escloopdepth < src.Escloopdepth || level < 0 && dst == &e.funcParam && haspointers(src.Type) 1293 1294 switch src.Op { 1295 case ONAME: 1296 if src.Class == PPARAM && (leaks || dst.Escloopdepth < 0) && src.Esc != EscHeap { 1297 src.Esc = EscScope 1298 if Debug['m'] != 0 { 1299 Warnl(int(src.Lineno), "leaking param: %v", Nconv(src, obj.FmtShort)) 1300 } 1301 } 1302 1303 // Treat a PPARAMREF closure variable as equivalent to the 1304 // original variable. 1305 if src.Class == PPARAMREF { 1306 if leaks && Debug['m'] != 0 { 1307 Warnl(int(src.Lineno), "leaking closure reference %v", Nconv(src, obj.FmtShort)) 1308 } 1309 escwalk(e, level, dst, src.Closure) 1310 } 1311 1312 case OPTRLIT, OADDR: 1313 if leaks { 1314 src.Esc = EscHeap 1315 addrescapes(src.Left) 1316 if Debug['m'] != 0 { 1317 p := src 1318 if p.Left.Op == OCLOSURE { 1319 p = p.Left // merely to satisfy error messages in tests 1320 } 1321 Warnl(int(src.Lineno), "%v escapes to heap", Nconv(p, obj.FmtShort)) 1322 } 1323 } 1324 1325 newlevel := level 1326 if level > MinLevel { 1327 newlevel-- 1328 } 1329 escwalk(e, newlevel, dst, src.Left) 1330 1331 case OARRAYLIT: 1332 if Isfixedarray(src.Type) { 1333 break 1334 } 1335 fallthrough 1336 1337 // fall through 1338 case ODDDARG, 1339 OMAKECHAN, 1340 OMAKEMAP, 1341 OMAKESLICE, 1342 OARRAYRUNESTR, 1343 OARRAYBYTESTR, 1344 OSTRARRAYRUNE, 1345 OSTRARRAYBYTE, 1346 OADDSTR, 1347 OMAPLIT, 1348 ONEW, 1349 OCLOSURE, 1350 OCALLPART, 1351 ORUNESTR, 1352 OCONVIFACE: 1353 if leaks { 1354 src.Esc = EscHeap 1355 if Debug['m'] != 0 { 1356 Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort)) 1357 } 1358 } 1359 1360 case ODOT, 1361 OSLICE, 1362 OSLICEARR, 1363 OSLICE3, 1364 OSLICE3ARR, 1365 OSLICESTR: 1366 escwalk(e, level, dst, src.Left) 1367 1368 case OINDEX: 1369 if Isfixedarray(src.Left.Type) { 1370 escwalk(e, level, dst, src.Left) 1371 break 1372 } 1373 fallthrough 1374 1375 // fall through 1376 case ODOTPTR, OINDEXMAP, OIND: 1377 newlevel := level 1378 1379 if level > MinLevel { 1380 newlevel++ 1381 } 1382 escwalk(e, newlevel, dst, src.Left) 1383 } 1384 1385 recurse: 1386 for ll := src.Escflowsrc; ll != nil; ll = ll.Next { 1387 escwalk(e, level, dst, ll.N) 1388 } 1389 1390 e.pdepth-- 1391 } 1392 1393 func esctag(e *EscState, func_ *Node) { 1394 func_.Esc = EscFuncTagged 1395 1396 // External functions are assumed unsafe, 1397 // unless //go:noescape is given before the declaration. 1398 if func_.Nbody == nil { 1399 if func_.Noescape { 1400 for t := getinargx(func_.Type).Type; t != nil; t = t.Down { 1401 if haspointers(t.Type) { 1402 t.Note = mktag(EscNone) 1403 } 1404 } 1405 } 1406 1407 return 1408 } 1409 1410 savefn := Curfn 1411 Curfn = func_ 1412 1413 for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { 1414 if ll.N.Op != ONAME || ll.N.Class != PPARAM { 1415 continue 1416 } 1417 1418 switch ll.N.Esc & EscMask { 1419 case EscNone, // not touched by escflood 1420 EscReturn: 1421 if haspointers(ll.N.Type) { // don't bother tagging for scalars 1422 ll.N.Paramfld.Note = mktag(int(ll.N.Esc)) 1423 } 1424 1425 case EscHeap, // touched by escflood, moved to heap 1426 EscScope: // touched by escflood, value leaves scope 1427 break 1428 } 1429 } 1430 1431 Curfn = savefn 1432 }