github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/compile/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 "fmt" 9 "strconv" 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 type bottomUpVisitor struct { 37 analyze func([]*Node, bool) 38 visitgen uint32 39 nodeID map[*Node]uint32 40 stack []*Node 41 } 42 43 // visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list. 44 // It calls analyze with successive groups of functions, working from 45 // the bottom of the call graph upward. Each time analyze is called with 46 // a list of functions, every function on that list only calls other functions 47 // on the list or functions that have been passed in previous invocations of 48 // analyze. Closures appear in the same list as their outer functions. 49 // The lists are as short as possible while preserving those requirements. 50 // (In a typical program, many invocations of analyze will be passed just 51 // a single function.) The boolean argument 'recursive' passed to analyze 52 // specifies whether the functions on the list are mutually recursive. 53 // If recursive is false, the list consists of only a single function and its closures. 54 // If recursive is true, the list may still contain only a single function, 55 // if that function is itself recursive. 56 func visitBottomUp(list []*Node, analyze func(list []*Node, recursive bool)) { 57 var v bottomUpVisitor 58 v.analyze = analyze 59 v.nodeID = make(map[*Node]uint32) 60 for _, n := range list { 61 if n.Op == ODCLFUNC && n.Func.FCurfn == nil { 62 v.visit(n) 63 } 64 } 65 } 66 67 func (v *bottomUpVisitor) visit(n *Node) uint32 { 68 if id := v.nodeID[n]; id > 0 { 69 // already visited 70 return id 71 } 72 73 v.visitgen++ 74 id := v.visitgen 75 v.nodeID[n] = id 76 v.visitgen++ 77 min := v.visitgen 78 79 v.stack = append(v.stack, n) 80 min = v.visitcodelist(n.Nbody, min) 81 if (min == id || min == id+1) && n.Func.FCurfn == nil { 82 // This node is the root of a strongly connected component. 83 84 // The original min passed to visitcodelist was n->walkgen+1. 85 // If visitcodelist found its way back to n->walkgen, then this 86 // block is a set of mutually recursive functions. 87 // Otherwise it's just a lone function that does not recurse. 88 recursive := min == id 89 90 // Remove connected component from stack. 91 // Mark walkgen so that future visits return a large number 92 // so as not to affect the caller's min. 93 94 var i int 95 for i = len(v.stack) - 1; i >= 0; i-- { 96 x := v.stack[i] 97 if x == n { 98 break 99 } 100 v.nodeID[x] = ^uint32(0) 101 } 102 v.nodeID[n] = ^uint32(0) 103 block := v.stack[i:] 104 // Run escape analysis on this set of functions. 105 v.stack = v.stack[:i] 106 v.analyze(block, recursive) 107 } 108 109 return min 110 } 111 112 func (v *bottomUpVisitor) visitcodelist(l Nodes, min uint32) uint32 { 113 for _, n := range l.Slice() { 114 min = v.visitcode(n, min) 115 } 116 return min 117 } 118 119 func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 { 120 if n == nil { 121 return min 122 } 123 124 min = v.visitcodelist(n.Ninit, min) 125 min = v.visitcode(n.Left, min) 126 min = v.visitcode(n.Right, min) 127 min = v.visitcodelist(n.List, min) 128 min = v.visitcodelist(n.Nbody, min) 129 min = v.visitcodelist(n.Rlist, min) 130 131 if n.Op == OCALLFUNC || n.Op == OCALLMETH { 132 fn := n.Left 133 if n.Op == OCALLMETH { 134 fn = n.Left.Sym.Def 135 } 136 if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Name.Defn != nil { 137 m := v.visit(fn.Name.Defn) 138 if m < min { 139 min = m 140 } 141 } 142 } 143 144 if n.Op == OCLOSURE { 145 m := v.visit(n.Func.Closure) 146 if m < min { 147 min = m 148 } 149 } 150 151 return min 152 } 153 154 // Escape analysis. 155 156 // An escape analysis pass for a set of functions. 157 // The analysis assumes that closures and the functions in which they 158 // appear are analyzed together, so that the aliasing between their 159 // variables can be modeled more precisely. 160 // 161 // First escfunc, esc and escassign recurse over the ast of each 162 // function to dig out flow(dst,src) edges between any 163 // pointer-containing nodes and store them in dst->escflowsrc. For 164 // variables assigned to a variable in an outer scope or used as a 165 // return value, they store a flow(theSink, src) edge to a fake node 166 // 'the Sink'. For variables referenced in closures, an edge 167 // flow(closure, &var) is recorded and the flow of a closure itself to 168 // an outer scope is tracked the same way as other variables. 169 // 170 // Then escflood walks the graph starting at theSink and tags all 171 // variables of it can reach an & node as escaping and all function 172 // parameters it can reach as leaking. 173 // 174 // If a value's address is taken but the address does not escape, 175 // then the value can stay on the stack. If the value new(T) does 176 // not escape, then new(T) can be rewritten into a stack allocation. 177 // The same is true of slice literals. 178 // 179 // If optimizations are disabled (-N), this code is not used. 180 // Instead, the compiler assumes that any value whose address 181 // is taken without being immediately dereferenced 182 // needs to be moved to the heap, and new(T) and slice 183 // literals are always real allocations. 184 185 func escapes(all []*Node) { 186 visitBottomUp(all, escAnalyze) 187 } 188 189 const ( 190 EscFuncUnknown = 0 + iota 191 EscFuncPlanned 192 EscFuncStarted 193 EscFuncTagged 194 ) 195 196 // There appear to be some loops in the escape graph, causing 197 // arbitrary recursion into deeper and deeper levels. 198 // Cut this off safely by making minLevel sticky: once you 199 // get that deep, you cannot go down any further but you also 200 // cannot go up any further. This is a conservative fix. 201 // Making minLevel smaller (more negative) would handle more 202 // complex chains of indirections followed by address-of operations, 203 // at the cost of repeating the traversal once for each additional 204 // allowed level when a loop is encountered. Using -2 suffices to 205 // pass all the tests we have written so far, which we assume matches 206 // the level of complexity we want the escape analysis code to handle. 207 const ( 208 MinLevel = -2 209 ) 210 211 // A Level encodes the reference state and context applied to 212 // (stack, heap) allocated memory. 213 // 214 // value is the overall sum of *(1) and &(-1) operations encountered 215 // along a path from a destination (sink, return value) to a source 216 // (allocation, parameter). 217 // 218 // suffixValue is the maximum-copy-started-suffix-level applied to a sink. 219 // For example: 220 // sink = x.left.left --> level=2, x is dereferenced twice and does not escape to sink. 221 // sink = &Node{x} --> level=-1, x is accessible from sink via one "address of" 222 // sink = &Node{&Node{x}} --> level=-2, x is accessible from sink via two "address of" 223 // sink = &Node{&Node{x.left}} --> level=-1, but x is NOT accessible from sink because it was indirected and then copied. 224 // (The copy operations are sometimes implicit in the source code; in this case, 225 // value of x.left was copied into a field of a newly allocated Node) 226 // 227 // There's one of these for each Node, and the integer values 228 // rarely exceed even what can be stored in 4 bits, never mind 8. 229 type Level struct { 230 value, suffixValue int8 231 } 232 233 func (l Level) int() int { 234 return int(l.value) 235 } 236 237 func levelFrom(i int) Level { 238 if i <= MinLevel { 239 return Level{value: MinLevel} 240 } 241 return Level{value: int8(i)} 242 } 243 244 func satInc8(x int8) int8 { 245 if x == 127 { 246 return 127 247 } 248 return x + 1 249 } 250 251 func min8(a, b int8) int8 { 252 if a < b { 253 return a 254 } 255 return b 256 } 257 258 func max8(a, b int8) int8 { 259 if a > b { 260 return a 261 } 262 return b 263 } 264 265 // inc returns the level l + 1, representing the effect of an indirect (*) operation. 266 func (l Level) inc() Level { 267 if l.value <= MinLevel { 268 return Level{value: MinLevel} 269 } 270 return Level{value: satInc8(l.value), suffixValue: satInc8(l.suffixValue)} 271 } 272 273 // dec returns the level l - 1, representing the effect of an address-of (&) operation. 274 func (l Level) dec() Level { 275 if l.value <= MinLevel { 276 return Level{value: MinLevel} 277 } 278 return Level{value: l.value - 1, suffixValue: l.suffixValue - 1} 279 } 280 281 // copy returns the level for a copy of a value with level l. 282 func (l Level) copy() Level { 283 return Level{value: l.value, suffixValue: max8(l.suffixValue, 0)} 284 } 285 286 func (l1 Level) min(l2 Level) Level { 287 return Level{ 288 value: min8(l1.value, l2.value), 289 suffixValue: min8(l1.suffixValue, l2.suffixValue)} 290 } 291 292 // guaranteedDereference returns the number of dereferences 293 // applied to a pointer before addresses are taken/generated. 294 // This is the maximum level computed from path suffixes starting 295 // with copies where paths flow from destination to source. 296 func (l Level) guaranteedDereference() int { 297 return int(l.suffixValue) 298 } 299 300 // An EscStep documents one step in the path from memory 301 // that is heap allocated to the (alleged) reason for the 302 // heap allocation. 303 type EscStep struct { 304 src, dst *Node // the endpoints of this edge in the escape-to-heap chain. 305 parent *EscStep // used in flood to record path 306 why string // explanation for this step in the escape-to-heap chain 307 busy bool // used in prevent to snip cycles. 308 } 309 310 type NodeEscState struct { 311 Curfn *Node 312 Escflowsrc []EscStep // flow(this, src) 313 Escretval Nodes // on OCALLxxx, list of dummy return values 314 Escloopdepth int32 // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes 315 Esclevel Level 316 Walkgen uint32 317 Maxextraloopdepth int32 318 } 319 320 func (e *EscState) nodeEscState(n *Node) *NodeEscState { 321 if nE, ok := n.Opt().(*NodeEscState); ok { 322 return nE 323 } 324 if n.Opt() != nil { 325 Fatalf("nodeEscState: opt in use (%T)", n.Opt()) 326 } 327 nE := new(NodeEscState) 328 nE.Curfn = Curfn 329 n.SetOpt(nE) 330 e.opts = append(e.opts, n) 331 return nE 332 } 333 334 func (e *EscState) track(n *Node) { 335 if Curfn == nil { 336 Fatalf("EscState.track: Curfn nil") 337 } 338 n.Esc = EscNone // until proven otherwise 339 nE := e.nodeEscState(n) 340 nE.Escloopdepth = e.loopdepth 341 e.noesc = append(e.noesc, n) 342 } 343 344 // Escape constants are numbered in order of increasing "escapiness" 345 // to help make inferences be monotonic. With the exception of 346 // EscNever which is sticky, eX < eY means that eY is more exposed 347 // than eX, and hence replaces it in a conservative analysis. 348 const ( 349 EscUnknown = iota 350 EscNone // Does not escape to heap, result, or parameters. 351 EscReturn // Is returned or reachable from returned. 352 EscScope // Allocated in an inner loop scope, assigned to an outer loop scope, 353 // which allows the construction of non-escaping but arbitrarily large linked 354 // data structures (i.e., not eligible for allocation in a fixed-size stack frame). 355 EscHeap // Reachable from the heap 356 EscNever // By construction will not escape. 357 EscBits = 3 358 EscMask = (1 << EscBits) - 1 359 EscContentEscapes = 1 << EscBits // value obtained by indirect of parameter escapes to heap 360 EscReturnBits = EscBits + 1 361 // Node.esc encoding = | escapeReturnEncoding:(width-4) | contentEscapes:1 | escEnum:3 362 ) 363 364 // escMax returns the maximum of an existing escape value 365 // (and its additional parameter flow flags) and a new escape type. 366 func escMax(e, etype uint16) uint16 { 367 if e&EscMask >= EscScope { 368 // normalize 369 if e&^EscMask != 0 { 370 Fatalf("Escape information had unexpected return encoding bits (w/ EscScope, EscHeap, EscNever), e&EscMask=%v", e&EscMask) 371 } 372 } 373 if e&EscMask > etype { 374 return e 375 } 376 if etype == EscNone || etype == EscReturn { 377 return (e &^ EscMask) | etype 378 } 379 return etype 380 } 381 382 // For each input parameter to a function, the escapeReturnEncoding describes 383 // how the parameter may leak to the function's outputs. This is currently the 384 // "level" of the leak where level is 0 or larger (negative level means stored into 385 // something whose address is returned -- but that implies stored into the heap, 386 // hence EscHeap, which means that the details are not currently relevant. ) 387 const ( 388 bitsPerOutputInTag = 3 // For each output, the number of bits for a tag 389 bitsMaskForTag = uint16(1<<bitsPerOutputInTag) - 1 // The bit mask to extract a single tag. 390 maxEncodedLevel = int(bitsMaskForTag - 1) // The largest level that can be stored in a tag. 391 ) 392 393 type EscState struct { 394 // Fake node that all 395 // - return values and output variables 396 // - parameters on imported functions not marked 'safe' 397 // - assignments to global variables 398 // flow to. 399 theSink Node 400 401 dsts []*Node // all dst nodes 402 loopdepth int32 // for detecting nested loop scopes 403 pdepth int // for debug printing in recursions. 404 dstcount int // diagnostic 405 edgecount int // diagnostic 406 noesc []*Node // list of possible non-escaping nodes, for printing 407 recursive bool // recursive function or group of mutually recursive functions. 408 opts []*Node // nodes with .Opt initialized 409 walkgen uint32 410 } 411 412 func (e *EscState) stepWalk(dst, src *Node, why string, parent *EscStep) *EscStep { 413 // TODO: keep a cache of these, mark entry/exit in escwalk to avoid allocation 414 // Or perhaps never mind, since it is disabled unless printing is on. 415 // We may want to revisit this, since the EscStep nodes would make 416 // an excellent replacement for the poorly-separated graph-build/graph-flood 417 // stages. 418 if Debug['m'] == 0 { 419 return nil 420 } 421 return &EscStep{src: src, dst: dst, why: why, parent: parent} 422 } 423 424 func (e *EscState) stepAssign(step *EscStep, dst, src *Node, why string) *EscStep { 425 if Debug['m'] == 0 { 426 return nil 427 } 428 if step != nil { // Caller may have known better. 429 return step 430 } 431 return &EscStep{src: src, dst: dst, why: why} 432 } 433 434 // funcSym returns fn.Func.Nname.Sym if no nils are encountered along the way. 435 func funcSym(fn *Node) *Sym { 436 if fn == nil || fn.Func.Nname == nil { 437 return nil 438 } 439 return fn.Func.Nname.Sym 440 } 441 442 // curfnSym returns n.Curfn.Nname.Sym if no nils are encountered along the way. 443 func (e *EscState) curfnSym(n *Node) *Sym { 444 nE := e.nodeEscState(n) 445 return funcSym(nE.Curfn) 446 } 447 448 func escAnalyze(all []*Node, recursive bool) { 449 var es EscState 450 e := &es 451 e.theSink.Op = ONAME 452 e.theSink.Orig = &e.theSink 453 e.theSink.Class = PEXTERN 454 e.theSink.Sym = Lookup(".sink") 455 e.nodeEscState(&e.theSink).Escloopdepth = -1 456 e.recursive = recursive 457 458 for _, n := range all { 459 if n.Op == ODCLFUNC { 460 n.Esc = EscFuncPlanned 461 } 462 } 463 464 // flow-analyze functions 465 for _, n := range all { 466 if n.Op == ODCLFUNC { 467 escfunc(e, n) 468 } 469 } 470 471 // print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount); 472 473 // visit the upstream of each dst, mark address nodes with 474 // addrescapes, mark parameters unsafe 475 for _, n := range e.dsts { 476 escflood(e, n) 477 } 478 479 // for all top level functions, tag the typenodes corresponding to the param nodes 480 for _, n := range all { 481 if n.Op == ODCLFUNC { 482 esctag(e, n) 483 } 484 } 485 486 if Debug['m'] != 0 { 487 for _, n := range e.noesc { 488 if n.Esc == EscNone { 489 Warnl(n.Lineno, "%v %v does not escape", e.curfnSym(n), Nconv(n, FmtShort)) 490 } 491 } 492 } 493 for _, x := range e.opts { 494 x.SetOpt(nil) 495 } 496 } 497 498 func escfunc(e *EscState, func_ *Node) { 499 // print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":""); 500 if func_.Esc != 1 { 501 Fatalf("repeat escfunc %v", func_.Func.Nname) 502 } 503 func_.Esc = EscFuncStarted 504 505 saveld := e.loopdepth 506 e.loopdepth = 1 507 savefn := Curfn 508 Curfn = func_ 509 510 for _, ln := range Curfn.Func.Dcl { 511 if ln.Op != ONAME { 512 continue 513 } 514 llNE := e.nodeEscState(ln) 515 switch ln.Class { 516 // out params are in a loopdepth between the sink and all local variables 517 case PPARAMOUT: 518 llNE.Escloopdepth = 0 519 520 case PPARAM: 521 llNE.Escloopdepth = 1 522 if ln.Type != nil && !haspointers(ln.Type) { 523 break 524 } 525 if len(Curfn.Nbody.Slice()) == 0 && !Curfn.Noescape { 526 ln.Esc = EscHeap 527 } else { 528 ln.Esc = EscNone // prime for escflood later 529 } 530 e.noesc = append(e.noesc, ln) 531 } 532 } 533 534 // in a mutually recursive group we lose track of the return values 535 if e.recursive { 536 for _, ln := range Curfn.Func.Dcl { 537 if ln.Op == ONAME && ln.Class == PPARAMOUT { 538 escflows(e, &e.theSink, ln, e.stepAssign(nil, ln, ln, "returned from recursive function")) 539 } 540 } 541 } 542 543 escloopdepthlist(e, Curfn.Nbody) 544 esclist(e, Curfn.Nbody, Curfn) 545 Curfn = savefn 546 e.loopdepth = saveld 547 } 548 549 // Mark labels that have no backjumps to them as not increasing e->loopdepth. 550 // Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat 551 // and set it to one of the following two. Then in esc we'll clear it again. 552 var looping Label 553 554 var nonlooping Label 555 556 func escloopdepthlist(e *EscState, l Nodes) { 557 for _, n := range l.Slice() { 558 escloopdepth(e, n) 559 } 560 } 561 562 func escloopdepth(e *EscState, n *Node) { 563 if n == nil { 564 return 565 } 566 567 escloopdepthlist(e, n.Ninit) 568 569 switch n.Op { 570 case OLABEL: 571 if n.Left == nil || n.Left.Sym == nil { 572 Fatalf("esc:label without label: %v", Nconv(n, FmtSign)) 573 } 574 575 // Walk will complain about this label being already defined, but that's not until 576 // after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc 577 // if(n->left->sym->label != nil) 578 // fatal("escape analysis messed up analyzing label: %+N", n); 579 n.Left.Sym.Label = &nonlooping 580 581 case OGOTO: 582 if n.Left == nil || n.Left.Sym == nil { 583 Fatalf("esc:goto without label: %v", Nconv(n, FmtSign)) 584 } 585 586 // If we come past one that's uninitialized, this must be a (harmless) forward jump 587 // but if it's set to nonlooping the label must have preceded this goto. 588 if n.Left.Sym.Label == &nonlooping { 589 n.Left.Sym.Label = &looping 590 } 591 } 592 593 escloopdepth(e, n.Left) 594 escloopdepth(e, n.Right) 595 escloopdepthlist(e, n.List) 596 escloopdepthlist(e, n.Nbody) 597 escloopdepthlist(e, n.Rlist) 598 } 599 600 func esclist(e *EscState, l Nodes, up *Node) { 601 for _, n := range l.Slice() { 602 esc(e, n, up) 603 } 604 } 605 606 func esc(e *EscState, n *Node, up *Node) { 607 if n == nil { 608 return 609 } 610 if n.Type == structkey { 611 // This is the left side of x:y in a struct literal. 612 // x is syntax, not an expression. 613 // See #14405. 614 return 615 } 616 617 lno := setlineno(n) 618 619 // ninit logically runs at a different loopdepth than the rest of the for loop. 620 esclist(e, n.Ninit, n) 621 622 if n.Op == OFOR || n.Op == ORANGE { 623 e.loopdepth++ 624 } 625 626 // type switch variables have no ODCL. 627 // process type switch as declaration. 628 // must happen before processing of switch body, 629 // so before recursion. 630 if n.Op == OSWITCH && n.Left != nil && n.Left.Op == OTYPESW { 631 for _, n1 := range n.List.Slice() { // cases 632 // it.N().Rlist is the variable per case 633 if n1.Rlist.Len() != 0 { 634 e.nodeEscState(n1.Rlist.First()).Escloopdepth = e.loopdepth 635 } 636 } 637 } 638 639 // Big stuff escapes unconditionally 640 // "Big" conditions that were scattered around in walk have been gathered here 641 if n.Esc != EscHeap && n.Type != nil && 642 (n.Type.Width > MaxStackVarSize || 643 n.Op == ONEW && n.Type.Elem().Width >= 1<<16 || 644 n.Op == OMAKESLICE && !isSmallMakeSlice(n)) { 645 if Debug['m'] > 2 { 646 Warnl(n.Lineno, "%v is too large for stack", n) 647 } 648 n.Esc = EscHeap 649 addrescapes(n) 650 escassignSinkNilWhy(e, n, n, "too large for stack") // TODO category: tooLarge 651 } 652 653 esc(e, n.Left, n) 654 esc(e, n.Right, n) 655 esclist(e, n.Nbody, n) 656 esclist(e, n.List, n) 657 esclist(e, n.Rlist, n) 658 659 if n.Op == OFOR || n.Op == ORANGE { 660 e.loopdepth-- 661 } 662 663 if Debug['m'] > 2 { 664 fmt.Printf("%v:[%d] %v esc: %v\n", linestr(lineno), e.loopdepth, funcSym(Curfn), n) 665 } 666 667 switch n.Op { 668 // Record loop depth at declaration. 669 case ODCL: 670 if n.Left != nil { 671 e.nodeEscState(n.Left).Escloopdepth = e.loopdepth 672 } 673 674 case OLABEL: 675 if n.Left.Sym.Label == &nonlooping { 676 if Debug['m'] > 2 { 677 fmt.Printf("%v:%v non-looping label\n", linestr(lineno), n) 678 } 679 } else if n.Left.Sym.Label == &looping { 680 if Debug['m'] > 2 { 681 fmt.Printf("%v: %v looping label\n", linestr(lineno), n) 682 } 683 e.loopdepth++ 684 } 685 686 // See case OLABEL in escloopdepth above 687 // else if(n->left->sym->label == nil) 688 // fatal("escape analysis missed or messed up a label: %+N", n); 689 690 n.Left.Sym.Label = nil 691 692 case ORANGE: 693 if n.List.Len() >= 2 { 694 // Everything but fixed array is a dereference. 695 696 // If fixed array is really the address of fixed array, 697 // it is also a dereference, because it is implicitly 698 // dereferenced (see #12588) 699 if n.Type.IsArray() && 700 !(n.Right.Type.IsPtr() && Eqtype(n.Right.Type.Elem(), n.Type)) { 701 escassignNilWhy(e, n.List.Second(), n.Right, "range") 702 } else { 703 escassignDereference(e, n.List.Second(), n.Right, e.stepAssign(nil, n.List.Second(), n.Right, "range-deref")) 704 } 705 } 706 707 case OSWITCH: 708 if n.Left != nil && n.Left.Op == OTYPESW { 709 for _, n2 := range n.List.Slice() { 710 // cases 711 // n.Left.Right is the argument of the .(type), 712 // it.N().Rlist is the variable per case 713 if n2.Rlist.Len() != 0 { 714 escassignNilWhy(e, n2.Rlist.First(), n.Left.Right, "switch case") 715 } 716 } 717 } 718 719 // Filter out the following special case. 720 // 721 // func (b *Buffer) Foo() { 722 // n, m := ... 723 // b.buf = b.buf[n:m] 724 // } 725 // 726 // This assignment is a no-op for escape analysis, 727 // it does not store any new pointers into b that were not already there. 728 // However, without this special case b will escape, because we assign to OIND/ODOTPTR. 729 case OAS, OASOP, OASWB: 730 if (n.Left.Op == OIND || n.Left.Op == ODOTPTR) && n.Left.Left.Op == ONAME && // dst is ONAME dereference 731 (n.Right.Op == OSLICE || n.Right.Op == OSLICE3 || n.Right.Op == OSLICESTR) && // src is slice operation 732 (n.Right.Left.Op == OIND || n.Right.Left.Op == ODOTPTR) && n.Right.Left.Left.Op == ONAME && // slice is applied to ONAME dereference 733 n.Left.Left == n.Right.Left.Left { // dst and src reference the same base ONAME 734 735 // Here we also assume that the statement will not contain calls, 736 // that is, that order will move any calls to init. 737 // Otherwise base ONAME value could change between the moments 738 // when we evaluate it for dst and for src. 739 // 740 // Note, this optimization does not apply to OSLICEARR, 741 // because it does introduce a new pointer into b that was not already there 742 // (pointer to b itself). After such assignment, if b contents escape, 743 // b escapes as well. If we ignore such OSLICEARR, we will conclude 744 // that b does not escape when b contents do. 745 if Debug['m'] != 0 { 746 Warnl(n.Lineno, "%v ignoring self-assignment to %v", e.curfnSym(n), Nconv(n.Left, FmtShort)) 747 } 748 749 break 750 } 751 752 escassign(e, n.Left, n.Right, nil) 753 754 case OAS2: // x,y = a,b 755 if n.List.Len() == n.Rlist.Len() { 756 rs := n.Rlist.Slice() 757 for i, n := range n.List.Slice() { 758 escassignNilWhy(e, n, rs[i], "assign-pair") 759 } 760 } 761 762 case OAS2RECV: // v, ok = <-ch 763 escassignNilWhy(e, n.List.First(), n.Rlist.First(), "assign-pair-receive") 764 case OAS2MAPR: // v, ok = m[k] 765 escassignNilWhy(e, n.List.First(), n.Rlist.First(), "assign-pair-mapr") 766 case OAS2DOTTYPE: // v, ok = x.(type) 767 escassignNilWhy(e, n.List.First(), n.Rlist.First(), "assign-pair-dot-type") 768 769 case OSEND: // ch <- x 770 escassignSinkNilWhy(e, n, n.Right, "send") 771 772 case ODEFER: 773 if e.loopdepth == 1 { // top level 774 break 775 } 776 // arguments leak out of scope 777 // TODO: leak to a dummy node instead 778 // defer f(x) - f and x escape 779 escassignSinkNilWhy(e, n, n.Left.Left, "defer func") 780 781 escassignSinkNilWhy(e, n, n.Left.Right, "defer func ...") // ODDDARG for call 782 for _, n4 := range n.Left.List.Slice() { 783 escassignSinkNilWhy(e, n, n4, "defer func arg") 784 } 785 786 case OPROC: 787 // go f(x) - f and x escape 788 escassignSinkNilWhy(e, n, n.Left.Left, "go func") 789 790 escassignSinkNilWhy(e, n, n.Left.Right, "go func ...") // ODDDARG for call 791 for _, n4 := range n.Left.List.Slice() { 792 escassignSinkNilWhy(e, n, n4, "go func arg") 793 } 794 795 case OCALLMETH, OCALLFUNC, OCALLINTER: 796 esccall(e, n, up) 797 798 // esccall already done on n->rlist->n. tie it's escretval to n->list 799 case OAS2FUNC: // x,y = f() 800 rs := e.nodeEscState(n.Rlist.First()).Escretval.Slice() 801 for i, n := range n.List.Slice() { 802 if i >= len(rs) { 803 break 804 } 805 escassignNilWhy(e, n, rs[i], "assign-pair-func-call") 806 } 807 if n.List.Len() != len(rs) { 808 Fatalf("esc oas2func") 809 } 810 811 case ORETURN: 812 ll := n.List 813 if n.List.Len() == 1 && Curfn.Type.Results().NumFields() > 1 { 814 // OAS2FUNC in disguise 815 // esccall already done on n->list->n 816 // tie n->list->n->escretval to curfn->dcl PPARAMOUT's 817 ll = e.nodeEscState(n.List.First()).Escretval 818 } 819 820 i := 0 821 for _, lrn := range Curfn.Func.Dcl { 822 if i >= ll.Len() { 823 break 824 } 825 if lrn.Op != ONAME || lrn.Class != PPARAMOUT { 826 continue 827 } 828 escassignNilWhy(e, lrn, ll.Index(i), "return") 829 i++ 830 } 831 832 if i < ll.Len() { 833 Fatalf("esc return list") 834 } 835 836 // Argument could leak through recover. 837 case OPANIC: 838 escassignSinkNilWhy(e, n, n.Left, "panic") 839 840 case OAPPEND: 841 if !n.Isddd { 842 for _, nn := range n.List.Slice()[1:] { 843 escassignSinkNilWhy(e, n, nn, "appended to slice") // lose track of assign to dereference 844 } 845 } else { 846 // append(slice1, slice2...) -- slice2 itself does not escape, but contents do. 847 slice2 := n.List.Second() 848 escassignDereference(e, &e.theSink, slice2, e.stepAssign(nil, n, slice2, "appended slice...")) // lose track of assign of dereference 849 if Debug['m'] > 3 { 850 Warnl(n.Lineno, "%v special treatment of append(slice1, slice2...) %v", e.curfnSym(n), Nconv(n, FmtShort)) 851 } 852 } 853 escassignDereference(e, &e.theSink, n.List.First(), e.stepAssign(nil, n, n.List.First(), "appendee slice")) // The original elements are now leaked, too 854 855 case OCOPY: 856 escassignDereference(e, &e.theSink, n.Right, e.stepAssign(nil, n, n.Right, "copied slice")) // lose track of assign of dereference 857 858 case OCONV, OCONVNOP: 859 escassignNilWhy(e, n, n.Left, "converted") 860 861 case OCONVIFACE: 862 e.track(n) 863 escassignNilWhy(e, n, n.Left, "interface-converted") 864 865 case OARRAYLIT: 866 why := "array literal element" 867 if n.Type.IsSlice() { 868 // Slice itself is not leaked until proven otherwise 869 e.track(n) 870 why = "slice literal element" 871 } 872 // Link values to array/slice 873 for _, n5 := range n.List.Slice() { 874 escassign(e, n, n5.Right, e.stepAssign(nil, n, n5.Right, why)) 875 } 876 877 // Link values to struct. 878 case OSTRUCTLIT: 879 for _, n6 := range n.List.Slice() { 880 escassignNilWhy(e, n, n6.Right, "struct literal element") 881 } 882 883 case OPTRLIT: 884 e.track(n) 885 886 // Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too. 887 escassignNilWhy(e, n, n.Left, "pointer literal [assign]") 888 889 case OCALLPART: 890 e.track(n) 891 892 // Contents make it to memory, lose track. 893 escassignSinkNilWhy(e, n, n.Left, "call part") 894 895 case OMAPLIT: 896 e.track(n) 897 // Keys and values make it to memory, lose track. 898 for _, n7 := range n.List.Slice() { 899 escassignSinkNilWhy(e, n, n7.Left, "map literal key") 900 escassignSinkNilWhy(e, n, n7.Right, "map literal value") 901 } 902 903 // Link addresses of captured variables to closure. 904 case OCLOSURE: 905 for _, v := range n.Func.Cvars.Slice() { 906 if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs 907 continue 908 } 909 a := v.Name.Param.Closure 910 if !v.Name.Byval { 911 a = Nod(OADDR, a, nil) 912 a.Lineno = v.Lineno 913 e.nodeEscState(a).Escloopdepth = e.loopdepth 914 a = typecheck(a, Erv) 915 } 916 917 escassignNilWhy(e, n, a, "captured by a closure") 918 } 919 fallthrough 920 921 case OMAKECHAN, 922 OMAKEMAP, 923 OMAKESLICE, 924 ONEW, 925 OARRAYRUNESTR, 926 OARRAYBYTESTR, 927 OSTRARRAYRUNE, 928 OSTRARRAYBYTE, 929 ORUNESTR: 930 e.track(n) 931 932 case OADDSTR: 933 e.track(n) 934 // Arguments of OADDSTR do not escape. 935 936 case OADDR: 937 // current loop depth is an upper bound on actual loop depth 938 // of addressed value. 939 e.track(n) 940 941 // for &x, use loop depth of x if known. 942 // it should always be known, but if not, be conservative 943 // and keep the current loop depth. 944 if n.Left.Op == ONAME { 945 switch n.Left.Class { 946 case PAUTO: 947 nE := e.nodeEscState(n) 948 leftE := e.nodeEscState(n.Left) 949 if leftE.Escloopdepth != 0 { 950 nE.Escloopdepth = leftE.Escloopdepth 951 } 952 953 // PPARAM is loop depth 1 always. 954 // PPARAMOUT is loop depth 0 for writes 955 // but considered loop depth 1 for address-of, 956 // so that writing the address of one result 957 // to another (or the same) result makes the 958 // first result move to the heap. 959 case PPARAM, PPARAMOUT: 960 nE := e.nodeEscState(n) 961 nE.Escloopdepth = 1 962 } 963 } 964 } 965 966 lineno = lno 967 } 968 969 // escassignNilWhy bundles a common case of 970 // escassign(e, dst, src, e.stepAssign(nil, dst, src, reason)) 971 func escassignNilWhy(e *EscState, dst, src *Node, reason string) { 972 var step *EscStep 973 if Debug['m'] != 0 { 974 step = e.stepAssign(nil, dst, src, reason) 975 } 976 escassign(e, dst, src, step) 977 } 978 979 // escassignSinkNilWhy bundles a common case of 980 // escassign(e, &e.theSink, src, e.stepAssign(nil, dst, src, reason)) 981 func escassignSinkNilWhy(e *EscState, dst, src *Node, reason string) { 982 var step *EscStep 983 if Debug['m'] != 0 { 984 step = e.stepAssign(nil, dst, src, reason) 985 } 986 escassign(e, &e.theSink, src, step) 987 } 988 989 // Assert that expr somehow gets assigned to dst, if non nil. for 990 // dst==nil, any name node expr still must be marked as being 991 // evaluated in curfn. For expr==nil, dst must still be examined for 992 // evaluations inside it (e.g *f(x) = y) 993 func escassign(e *EscState, dst, src *Node, step *EscStep) { 994 if isblank(dst) || dst == nil || src == nil || src.Op == ONONAME || src.Op == OXXX { 995 return 996 } 997 998 if Debug['m'] > 2 { 999 fmt.Printf("%v:[%d] %v escassign: %v(%v)[%v] = %v(%v)[%v]\n", 1000 linestr(lineno), e.loopdepth, funcSym(Curfn), 1001 Nconv(dst, FmtShort), Jconv(dst, FmtShort), Oconv(dst.Op, 0), 1002 Nconv(src, FmtShort), Jconv(src, FmtShort), Oconv(src.Op, 0)) 1003 } 1004 1005 setlineno(dst) 1006 1007 originalDst := dst 1008 dstwhy := "assigned" 1009 1010 // Analyze lhs of assignment. 1011 // Replace dst with e->theSink if we can't track it. 1012 switch dst.Op { 1013 default: 1014 Dump("dst", dst) 1015 Fatalf("escassign: unexpected dst") 1016 1017 case OARRAYLIT, 1018 OCLOSURE, 1019 OCONV, 1020 OCONVIFACE, 1021 OCONVNOP, 1022 OMAPLIT, 1023 OSTRUCTLIT, 1024 OPTRLIT, 1025 ODDDARG, 1026 OCALLPART: 1027 break 1028 1029 case ONAME: 1030 if dst.Class == PEXTERN { 1031 dstwhy = "assigned to top level variable" 1032 dst = &e.theSink 1033 } 1034 1035 case ODOT: // treat "dst.x = src" as "dst = src" 1036 escassign(e, dst.Left, src, e.stepAssign(step, originalDst, src, "dot-equals")) 1037 return 1038 1039 case OINDEX: 1040 if dst.Left.Type.IsArray() { 1041 escassign(e, dst.Left, src, e.stepAssign(step, originalDst, src, "array-element-equals")) 1042 return 1043 } 1044 1045 dstwhy = "slice-element-equals" 1046 dst = &e.theSink // lose track of dereference 1047 1048 case OIND: 1049 dstwhy = "star-equals" 1050 dst = &e.theSink // lose track of dereference 1051 1052 case ODOTPTR: 1053 dstwhy = "star-dot-equals" 1054 dst = &e.theSink // lose track of dereference 1055 1056 // lose track of key and value 1057 case OINDEXMAP: 1058 escassign(e, &e.theSink, dst.Right, e.stepAssign(nil, originalDst, src, "key of map put")) 1059 dstwhy = "value of map put" 1060 dst = &e.theSink 1061 } 1062 1063 lno := setlineno(src) 1064 e.pdepth++ 1065 1066 switch src.Op { 1067 case OADDR, // dst = &x 1068 OIND, // dst = *x 1069 ODOTPTR, // dst = (*x).f 1070 ONAME, 1071 OPARAM, 1072 ODDDARG, 1073 OPTRLIT, 1074 OARRAYLIT, 1075 OMAPLIT, 1076 OSTRUCTLIT, 1077 OMAKECHAN, 1078 OMAKEMAP, 1079 OMAKESLICE, 1080 OARRAYRUNESTR, 1081 OARRAYBYTESTR, 1082 OSTRARRAYRUNE, 1083 OSTRARRAYBYTE, 1084 OADDSTR, 1085 ONEW, 1086 OCALLPART, 1087 ORUNESTR, 1088 OCONVIFACE: 1089 escflows(e, dst, src, e.stepAssign(step, originalDst, src, dstwhy)) 1090 1091 case OCLOSURE: 1092 // OCLOSURE is lowered to OPTRLIT, 1093 // insert OADDR to account for the additional indirection. 1094 a := Nod(OADDR, src, nil) 1095 a.Lineno = src.Lineno 1096 e.nodeEscState(a).Escloopdepth = e.nodeEscState(src).Escloopdepth 1097 a.Type = Ptrto(src.Type) 1098 escflows(e, dst, a, e.stepAssign(nil, originalDst, src, dstwhy)) 1099 1100 // Flowing multiple returns to a single dst happens when 1101 // analyzing "go f(g())": here g() flows to sink (issue 4529). 1102 case OCALLMETH, OCALLFUNC, OCALLINTER: 1103 for _, n := range e.nodeEscState(src).Escretval.Slice() { 1104 escflows(e, dst, n, e.stepAssign(nil, originalDst, n, dstwhy)) 1105 } 1106 1107 // A non-pointer escaping from a struct does not concern us. 1108 case ODOT: 1109 if src.Type != nil && !haspointers(src.Type) { 1110 break 1111 } 1112 fallthrough 1113 1114 // Conversions, field access, slice all preserve the input value. 1115 case OCONV, 1116 OCONVNOP, 1117 ODOTMETH, 1118 // treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC 1119 // iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here 1120 ODOTTYPE2, 1121 OSLICE, 1122 OSLICE3, 1123 OSLICEARR, 1124 OSLICE3ARR, 1125 OSLICESTR: 1126 // Conversions, field access, slice all preserve the input value. 1127 escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) 1128 1129 case ODOTTYPE: 1130 if src.Type != nil && !haspointers(src.Type) { 1131 break 1132 } 1133 escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) 1134 1135 case OAPPEND: 1136 // Append returns first argument. 1137 // Subsequent arguments are already leaked because they are operands to append. 1138 escassign(e, dst, src.List.First(), e.stepAssign(step, dst, src.List.First(), dstwhy)) 1139 1140 case OINDEX: 1141 // Index of array preserves input value. 1142 if src.Left.Type.IsArray() { 1143 escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) 1144 } else { 1145 escflows(e, dst, src, e.stepAssign(step, originalDst, src, dstwhy)) 1146 } 1147 1148 // Might be pointer arithmetic, in which case 1149 // the operands flow into the result. 1150 // TODO(rsc): Decide what the story is here. This is unsettling. 1151 case OADD, 1152 OSUB, 1153 OOR, 1154 OXOR, 1155 OMUL, 1156 ODIV, 1157 OMOD, 1158 OLSH, 1159 ORSH, 1160 OAND, 1161 OANDNOT, 1162 OPLUS, 1163 OMINUS, 1164 OCOM: 1165 escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) 1166 1167 escassign(e, dst, src.Right, e.stepAssign(step, originalDst, src, dstwhy)) 1168 } 1169 1170 e.pdepth-- 1171 lineno = lno 1172 } 1173 1174 // Common case for escapes is 16 bits 000000000xxxEEEE 1175 // where commonest cases for xxx encoding in-to-out pointer 1176 // flow are 000, 001, 010, 011 and EEEE is computed Esc bits. 1177 // Note width of xxx depends on value of constant 1178 // bitsPerOutputInTag -- expect 2 or 3, so in practice the 1179 // tag cache array is 64 or 128 long. Some entries will 1180 // never be populated. 1181 var tags [1 << (bitsPerOutputInTag + EscReturnBits)]string 1182 1183 // mktag returns the string representation for an escape analysis tag. 1184 func mktag(mask int) *string { 1185 switch mask & EscMask { 1186 case EscNone, EscReturn: 1187 break 1188 1189 default: 1190 Fatalf("escape mktag") 1191 } 1192 1193 if mask < len(tags) && tags[mask] != "" { 1194 return &tags[mask] 1195 } 1196 1197 s := fmt.Sprintf("esc:0x%x", mask) 1198 if mask < len(tags) { 1199 tags[mask] = s 1200 } 1201 return &s 1202 } 1203 1204 // parsetag decodes an escape analysis tag and returns the esc value. 1205 func parsetag(note *string) uint16 { 1206 if note == nil || !strings.HasPrefix(*note, "esc:") { 1207 return EscUnknown 1208 } 1209 n, _ := strconv.ParseInt((*note)[4:], 0, 0) 1210 em := uint16(n) 1211 if em == 0 { 1212 return EscNone 1213 } 1214 return em 1215 } 1216 1217 // describeEscape returns a string describing the escape tag. 1218 // The result is either one of {EscUnknown, EscNone, EscHeap} which all have no further annotation 1219 // or a description of parameter flow, which takes the form of an optional "contentToHeap" 1220 // indicating that the content of this parameter is leaked to the heap, followed by a sequence 1221 // of level encodings separated by spaces, one for each parameter, where _ means no flow, 1222 // = means direct flow, and N asterisks (*) encodes content (obtained by indirection) flow. 1223 // e.g., "contentToHeap _ =" means that a parameter's content (one or more dereferences) 1224 // escapes to the heap, the parameter does not leak to the first output, but does leak directly 1225 // to the second output (and if there are more than two outputs, there is no flow to those.) 1226 func describeEscape(em uint16) string { 1227 var s string 1228 if em&EscMask == EscUnknown { 1229 s = "EscUnknown" 1230 } 1231 if em&EscMask == EscNone { 1232 s = "EscNone" 1233 } 1234 if em&EscMask == EscHeap { 1235 s = "EscHeap" 1236 } 1237 if em&EscMask == EscReturn { 1238 s = "EscReturn" 1239 } 1240 if em&EscMask == EscScope { 1241 s = "EscScope" 1242 } 1243 if em&EscContentEscapes != 0 { 1244 if s != "" { 1245 s += " " 1246 } 1247 s += "contentToHeap" 1248 } 1249 for em >>= EscReturnBits; em != 0; em = em >> bitsPerOutputInTag { 1250 // See encoding description above 1251 if s != "" { 1252 s += " " 1253 } 1254 switch embits := em & bitsMaskForTag; embits { 1255 case 0: 1256 s += "_" 1257 case 1: 1258 s += "=" 1259 default: 1260 for i := uint16(0); i < embits-1; i++ { 1261 s += "*" 1262 } 1263 } 1264 1265 } 1266 return s 1267 } 1268 1269 // escassignfromtag models the input-to-output assignment flow of one of a function 1270 // calls arguments, where the flow is encoded in "note". 1271 func escassignfromtag(e *EscState, note *string, dsts Nodes, src *Node) uint16 { 1272 em := parsetag(note) 1273 if src.Op == OLITERAL { 1274 return em 1275 } 1276 1277 if Debug['m'] > 3 { 1278 fmt.Printf("%v::assignfromtag:: src=%v, em=%s\n", 1279 linestr(lineno), Nconv(src, FmtShort), describeEscape(em)) 1280 } 1281 1282 if em == EscUnknown { 1283 escassignSinkNilWhy(e, src, src, "passed to function[unknown]") 1284 return em 1285 } 1286 1287 if em == EscNone { 1288 return em 1289 } 1290 1291 // If content inside parameter (reached via indirection) 1292 // escapes to heap, mark as such. 1293 if em&EscContentEscapes != 0 { 1294 escassign(e, &e.theSink, e.addDereference(src), e.stepAssign(nil, src, src, "passed to function[content escapes]")) 1295 } 1296 1297 em0 := em 1298 dstsi := 0 1299 for em >>= EscReturnBits; em != 0 && dstsi < dsts.Len(); em = em >> bitsPerOutputInTag { 1300 // Prefer the lowest-level path to the reference (for escape purposes). 1301 // Two-bit encoding (for example. 1, 3, and 4 bits are other options) 1302 // 01 = 0-level 1303 // 10 = 1-level, (content escapes), 1304 // 11 = 2-level, (content of content escapes), 1305 embits := em & bitsMaskForTag 1306 if embits > 0 { 1307 n := src 1308 for i := uint16(0); i < embits-1; i++ { 1309 n = e.addDereference(n) // encode level>0 as indirections 1310 } 1311 escassign(e, dsts.Index(dstsi), n, e.stepAssign(nil, dsts.Index(dstsi), src, "passed-to-and-returned-from-function")) 1312 } 1313 dstsi++ 1314 } 1315 // If there are too many outputs to fit in the tag, 1316 // that is handled at the encoding end as EscHeap, 1317 // so there is no need to check here. 1318 1319 if em != 0 && dstsi >= dsts.Len() { 1320 Fatalf("corrupt esc tag %q or messed up escretval list\n", note) 1321 } 1322 return em0 1323 } 1324 1325 func escassignDereference(e *EscState, dst *Node, src *Node, step *EscStep) { 1326 if src.Op == OLITERAL { 1327 return 1328 } 1329 escassign(e, dst, e.addDereference(src), step) 1330 } 1331 1332 // addDereference constructs a suitable OIND note applied to src. 1333 // Because this is for purposes of escape accounting, not execution, 1334 // some semantically dubious node combinations are (currently) possible. 1335 func (e *EscState) addDereference(n *Node) *Node { 1336 ind := Nod(OIND, n, nil) 1337 e.nodeEscState(ind).Escloopdepth = e.nodeEscState(n).Escloopdepth 1338 ind.Lineno = n.Lineno 1339 t := n.Type 1340 if t.IsKind(Tptr) { 1341 // This should model our own sloppy use of OIND to encode 1342 // decreasing levels of indirection; i.e., "indirecting" an array 1343 // might yield the type of an element. To be enhanced... 1344 t = t.Elem() 1345 } 1346 ind.Type = t 1347 return ind 1348 } 1349 1350 // escNoteOutputParamFlow encodes maxEncodedLevel/.../1/0-level flow to the vargen'th parameter. 1351 // Levels greater than maxEncodedLevel are replaced with maxEncodedLevel. 1352 // If the encoding cannot describe the modified input level and output number, then EscHeap is returned. 1353 func escNoteOutputParamFlow(e uint16, vargen int32, level Level) uint16 { 1354 // Flow+level is encoded in two bits. 1355 // 00 = not flow, xx = level+1 for 0 <= level <= maxEncodedLevel 1356 // 16 bits for Esc allows 6x2bits or 4x3bits or 3x4bits if additional information would be useful. 1357 if level.int() <= 0 && level.guaranteedDereference() > 0 { 1358 return escMax(e|EscContentEscapes, EscNone) // At least one deref, thus only content. 1359 } 1360 if level.int() < 0 { 1361 return EscHeap 1362 } 1363 if level.int() > maxEncodedLevel { 1364 // Cannot encode larger values than maxEncodedLevel. 1365 level = levelFrom(maxEncodedLevel) 1366 } 1367 encoded := uint16(level.int() + 1) 1368 1369 shift := uint(bitsPerOutputInTag*(vargen-1) + EscReturnBits) 1370 old := (e >> shift) & bitsMaskForTag 1371 if old == 0 || encoded != 0 && encoded < old { 1372 old = encoded 1373 } 1374 1375 encodedFlow := old << shift 1376 if (encodedFlow>>shift)&bitsMaskForTag != old { 1377 // Encoding failure defaults to heap. 1378 return EscHeap 1379 } 1380 1381 return (e &^ (bitsMaskForTag << shift)) | encodedFlow 1382 } 1383 1384 func initEscretval(e *EscState, n *Node, fntype *Type) { 1385 i := 0 1386 nE := e.nodeEscState(n) 1387 nE.Escretval.Set(nil) // Suspect this is not nil for indirect calls. 1388 for _, t := range fntype.Results().Fields().Slice() { 1389 src := Nod(ONAME, nil, nil) 1390 buf := fmt.Sprintf(".out%d", i) 1391 i++ 1392 src.Sym = Lookup(buf) 1393 src.Type = t.Type 1394 src.Class = PAUTO 1395 src.Name.Curfn = Curfn 1396 e.nodeEscState(src).Escloopdepth = e.loopdepth 1397 src.Used = true 1398 src.Lineno = n.Lineno 1399 nE.Escretval.Append(src) 1400 } 1401 } 1402 1403 // This is a bit messier than fortunate, pulled out of esc's big 1404 // switch for clarity. We either have the paramnodes, which may be 1405 // connected to other things through flows or we have the parameter type 1406 // nodes, which may be marked "noescape". Navigating the ast is slightly 1407 // different for methods vs plain functions and for imported vs 1408 // this-package 1409 func esccall(e *EscState, n *Node, up *Node) { 1410 var fntype *Type 1411 var indirect bool 1412 var fn *Node 1413 switch n.Op { 1414 default: 1415 Fatalf("esccall") 1416 1417 case OCALLFUNC: 1418 fn = n.Left 1419 fntype = fn.Type 1420 indirect = fn.Op != ONAME || fn.Class != PFUNC 1421 1422 case OCALLMETH: 1423 fn = n.Left.Sym.Def 1424 if fn != nil { 1425 fntype = fn.Type 1426 } else { 1427 fntype = n.Left.Type 1428 } 1429 1430 case OCALLINTER: 1431 fntype = n.Left.Type 1432 indirect = true 1433 } 1434 1435 ll := n.List 1436 if n.List.Len() == 1 { 1437 a := n.List.First() 1438 if a.Type.IsStruct() && a.Type.Funarg { // f(g()). 1439 ll = e.nodeEscState(a).Escretval 1440 } 1441 } 1442 1443 if indirect { 1444 // We know nothing! 1445 // Leak all the parameters 1446 for _, n1 := range ll.Slice() { 1447 escassignSinkNilWhy(e, n, n1, "parameter to indirect call") 1448 if Debug['m'] > 3 { 1449 fmt.Printf("%v::esccall:: indirect call <- %v, untracked\n", linestr(lineno), Nconv(n1, FmtShort)) 1450 } 1451 } 1452 // Set up bogus outputs 1453 initEscretval(e, n, fntype) 1454 // If there is a receiver, it also leaks to heap. 1455 if n.Op != OCALLFUNC { 1456 t := fntype.Recv() 1457 src := n.Left.Left 1458 if haspointers(t.Type) { 1459 escassignSinkNilWhy(e, n, src, "receiver in indirect call") 1460 } 1461 } 1462 return 1463 } 1464 1465 nE := e.nodeEscState(n) 1466 if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && 1467 fn.Name.Defn != nil && len(fn.Name.Defn.Nbody.Slice()) != 0 && fn.Name.Param.Ntype != nil && fn.Name.Defn.Esc < EscFuncTagged { 1468 if Debug['m'] > 3 { 1469 fmt.Printf("%v::esccall:: %v in recursive group\n", linestr(lineno), Nconv(n, FmtShort)) 1470 } 1471 1472 // function in same mutually recursive group. Incorporate into flow graph. 1473 // print("esc local fn: %N\n", fn->ntype); 1474 if fn.Name.Defn.Esc == EscFuncUnknown || nE.Escretval.Len() != 0 { 1475 Fatalf("graph inconsistency") 1476 } 1477 1478 lls := ll.Slice() 1479 sawRcvr := false 1480 var src *Node 1481 for _, n2 := range fn.Name.Defn.Func.Dcl { 1482 switch n2.Class { 1483 case PPARAM: 1484 if n.Op != OCALLFUNC && !sawRcvr { 1485 escassignNilWhy(e, n2, n.Left.Left, "call receiver") 1486 sawRcvr = true 1487 continue 1488 } 1489 if len(lls) == 0 { 1490 continue 1491 } 1492 src = lls[0] 1493 if n2.Isddd && !n.Isddd { 1494 // Introduce ODDDARG node to represent ... allocation. 1495 src = Nod(ODDDARG, nil, nil) 1496 arr := typArray(n2.Type.Elem(), int64(len(lls))) 1497 src.Type = Ptrto(arr) // make pointer so it will be tracked 1498 src.Lineno = n.Lineno 1499 e.track(src) 1500 n.Right = src 1501 } 1502 escassignNilWhy(e, n2, src, "arg to recursive call") 1503 if src != lls[0] { 1504 // "..." arguments are untracked 1505 for _, n2 := range lls { 1506 if Debug['m'] > 3 { 1507 fmt.Printf("%v::esccall:: ... <- %v, untracked\n", linestr(lineno), Nconv(n2, FmtShort)) 1508 } 1509 escassignSinkNilWhy(e, src, n2, "... arg to recursive call") 1510 } 1511 // No more PPARAM processing, but keep 1512 // going for PPARAMOUT. 1513 lls = nil 1514 continue 1515 } 1516 lls = lls[1:] 1517 1518 case PPARAMOUT: 1519 nE.Escretval.Append(n2) 1520 } 1521 } 1522 1523 return 1524 } 1525 1526 // Imported or completely analyzed function. Use the escape tags. 1527 if nE.Escretval.Len() != 0 { 1528 Fatalf("esc already decorated call %v\n", Nconv(n, FmtSign)) 1529 } 1530 1531 if Debug['m'] > 3 { 1532 fmt.Printf("%v::esccall:: %v not recursive\n", linestr(lineno), Nconv(n, FmtShort)) 1533 } 1534 1535 // set up out list on this call node with dummy auto ONAMES in the current (calling) function. 1536 initEscretval(e, n, fntype) 1537 1538 // print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval); 1539 1540 // Receiver. 1541 if n.Op != OCALLFUNC { 1542 t := fntype.Recv() 1543 src := n.Left.Left 1544 if haspointers(t.Type) { 1545 escassignfromtag(e, t.Note, nE.Escretval, src) 1546 } 1547 } 1548 1549 var src *Node 1550 i := 0 1551 lls := ll.Slice() 1552 for t, it := IterFields(fntype.Params()); i < len(lls); i++ { 1553 src = lls[i] 1554 if t.Isddd && !n.Isddd { 1555 // Introduce ODDDARG node to represent ... allocation. 1556 src = Nod(ODDDARG, nil, nil) 1557 src.Lineno = n.Lineno 1558 arr := typArray(t.Type.Elem(), int64(len(lls)-i)) 1559 src.Type = Ptrto(arr) // make pointer so it will be tracked 1560 e.track(src) 1561 n.Right = src 1562 } 1563 1564 if haspointers(t.Type) { 1565 if escassignfromtag(e, t.Note, nE.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC { 1566 a := src 1567 for a.Op == OCONVNOP { 1568 a = a.Left 1569 } 1570 switch a.Op { 1571 // The callee has already been analyzed, so its arguments have esc tags. 1572 // The argument is marked as not escaping at all. 1573 // Record that fact so that any temporary used for 1574 // synthesizing this expression can be reclaimed when 1575 // the function returns. 1576 // This 'noescape' is even stronger than the usual esc == EscNone. 1577 // src->esc == EscNone means that src does not escape the current function. 1578 // src->noescape = 1 here means that src does not escape this statement 1579 // in the current function. 1580 case OCALLPART, 1581 OCLOSURE, 1582 ODDDARG, 1583 OARRAYLIT, 1584 OPTRLIT, 1585 OSTRUCTLIT: 1586 a.Noescape = true 1587 } 1588 } 1589 } 1590 1591 if src != lls[i] { 1592 // This occurs when function parameter type Isddd and n not Isddd 1593 break 1594 } 1595 t = it.Next() 1596 } 1597 1598 for ; i < len(lls); i++ { 1599 if Debug['m'] > 3 { 1600 fmt.Printf("%v::esccall:: ... <- %v\n", linestr(lineno), Nconv(lls[i], FmtShort)) 1601 } 1602 escassignNilWhy(e, src, lls[i], "arg to ...") // args to slice 1603 } 1604 } 1605 1606 // escflows records the link src->dst in dst, throwing out some quick wins, 1607 // and also ensuring that dst is noted as a flow destination. 1608 func escflows(e *EscState, dst, src *Node, why *EscStep) { 1609 if dst == nil || src == nil || dst == src { 1610 return 1611 } 1612 1613 // Don't bother building a graph for scalars. 1614 if src.Type != nil && !haspointers(src.Type) { 1615 return 1616 } 1617 1618 if Debug['m'] > 3 { 1619 fmt.Printf("%v::flows:: %v <- %v\n", linestr(lineno), Nconv(dst, FmtShort), Nconv(src, FmtShort)) 1620 } 1621 1622 dstE := e.nodeEscState(dst) 1623 if len(dstE.Escflowsrc) == 0 { 1624 e.dsts = append(e.dsts, dst) 1625 e.dstcount++ 1626 } 1627 1628 e.edgecount++ 1629 1630 if why == nil { 1631 dstE.Escflowsrc = append(dstE.Escflowsrc, EscStep{src: src}) 1632 } else { 1633 starwhy := *why 1634 starwhy.src = src // TODO: need to reconcile this w/ needs of explanations. 1635 dstE.Escflowsrc = append(dstE.Escflowsrc, starwhy) 1636 } 1637 } 1638 1639 // Whenever we hit a reference node, the level goes up by one, and whenever 1640 // we hit an OADDR, the level goes down by one. as long as we're on a level > 0 1641 // finding an OADDR just means we're following the upstream of a dereference, 1642 // so this address doesn't leak (yet). 1643 // If level == 0, it means the /value/ of this node can reach the root of this flood. 1644 // so if this node is an OADDR, its argument should be marked as escaping iff 1645 // its currfn/e->loopdepth are different from the flood's root. 1646 // Once an object has been moved to the heap, all of its upstream should be considered 1647 // escaping to the global scope. 1648 func escflood(e *EscState, dst *Node) { 1649 switch dst.Op { 1650 case ONAME, OCLOSURE: 1651 break 1652 1653 default: 1654 return 1655 } 1656 1657 dstE := e.nodeEscState(dst) 1658 if Debug['m'] > 2 { 1659 fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", e.walkgen, Nconv(dst, FmtShort), e.curfnSym(dst), dstE.Escloopdepth) 1660 } 1661 1662 for i, l := range dstE.Escflowsrc { 1663 e.walkgen++ 1664 dstE.Escflowsrc[i].parent = nil 1665 escwalk(e, levelFrom(0), dst, l.src, &dstE.Escflowsrc[i]) 1666 } 1667 } 1668 1669 // funcOutputAndInput reports whether dst and src correspond to output and input parameters of the same function. 1670 func funcOutputAndInput(dst, src *Node) bool { 1671 // Note if dst is marked as escaping, then "returned" is too weak. 1672 return dst.Op == ONAME && dst.Class == PPARAMOUT && 1673 src.Op == ONAME && src.Class == PPARAM && src.Name.Curfn == dst.Name.Curfn 1674 } 1675 1676 func (es *EscStep) describe(src *Node) { 1677 if Debug['m'] < 2 { 1678 return 1679 } 1680 step0 := es 1681 for step := step0; step != nil && !step.busy; step = step.parent { 1682 // TODO: We get cycles. Trigger is i = &i (where var i interface{}) 1683 step.busy = true 1684 // The trail is a little odd because of how the 1685 // graph is constructed. The link to the current 1686 // Node is parent.src unless parent is nil in which 1687 // case it is step.dst. 1688 nextDest := step.parent 1689 dst := step.dst 1690 if nextDest != nil { 1691 dst = nextDest.src 1692 } 1693 Warnl(src.Lineno, "\tfrom %s (%s) at %s", dst, step.why, dst.Line()) 1694 } 1695 for step := step0; step != nil && step.busy; step = step.parent { 1696 step.busy = false 1697 } 1698 } 1699 1700 const NOTALOOPDEPTH = -1 1701 1702 func escwalk(e *EscState, level Level, dst *Node, src *Node, step *EscStep) { 1703 escwalkBody(e, level, dst, src, step, NOTALOOPDEPTH) 1704 } 1705 1706 func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, extraloopdepth int32) { 1707 if src.Op == OLITERAL { 1708 return 1709 } 1710 srcE := e.nodeEscState(src) 1711 if srcE.Walkgen == e.walkgen { 1712 // Esclevels are vectors, do not compare as integers, 1713 // and must use "min" of old and new to guarantee 1714 // convergence. 1715 level = level.min(srcE.Esclevel) 1716 if level == srcE.Esclevel { 1717 // Have we been here already with an extraloopdepth, 1718 // or is the extraloopdepth provided no improvement on 1719 // what's already been seen? 1720 if srcE.Maxextraloopdepth >= extraloopdepth || srcE.Escloopdepth >= extraloopdepth { 1721 return 1722 } 1723 srcE.Maxextraloopdepth = extraloopdepth 1724 } 1725 } else { // srcE.Walkgen < e.walkgen -- first time, reset this. 1726 srcE.Maxextraloopdepth = NOTALOOPDEPTH 1727 } 1728 1729 srcE.Walkgen = e.walkgen 1730 srcE.Esclevel = level 1731 modSrcLoopdepth := srcE.Escloopdepth 1732 1733 if extraloopdepth > modSrcLoopdepth { 1734 modSrcLoopdepth = extraloopdepth 1735 } 1736 1737 if Debug['m'] > 2 { 1738 fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %v(%v) scope:%v[%d] extraloopdepth=%v\n", 1739 level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(src.Op, 0), Nconv(src, FmtShort), Jconv(src, FmtShort), e.curfnSym(src), srcE.Escloopdepth, extraloopdepth) 1740 } 1741 1742 e.pdepth++ 1743 1744 // Input parameter flowing to output parameter? 1745 var leaks bool 1746 var osrcesc uint16 // used to prevent duplicate error messages 1747 1748 dstE := e.nodeEscState(dst) 1749 if funcOutputAndInput(dst, src) && src.Esc&EscMask < EscScope && dst.Esc != EscHeap { 1750 // This case handles: 1751 // 1. return in 1752 // 2. return &in 1753 // 3. tmp := in; return &tmp 1754 // 4. return *in 1755 if Debug['m'] != 0 { 1756 if Debug['m'] <= 2 { 1757 Warnl(src.Lineno, "leaking param: %v to result %v level=%v", Nconv(src, FmtShort), dst.Sym, level.int()) 1758 step.describe(src) 1759 } else { 1760 Warnl(src.Lineno, "leaking param: %v to result %v level=%v", Nconv(src, FmtShort), dst.Sym, level) 1761 } 1762 } 1763 if src.Esc&EscMask != EscReturn { 1764 src.Esc = EscReturn | src.Esc&EscContentEscapes 1765 } 1766 src.Esc = escNoteOutputParamFlow(src.Esc, dst.Name.Vargen, level) 1767 goto recurse 1768 } 1769 1770 // If parameter content escapes to heap, set EscContentEscapes 1771 // Note minor confusion around escape from pointer-to-struct vs escape from struct 1772 if dst.Esc == EscHeap && 1773 src.Op == ONAME && src.Class == PPARAM && src.Esc&EscMask < EscScope && 1774 level.int() > 0 { 1775 src.Esc = escMax(EscContentEscapes|src.Esc, EscNone) 1776 if Debug['m'] != 0 { 1777 Warnl(src.Lineno, "mark escaped content: %v", Nconv(src, FmtShort)) 1778 step.describe(src) 1779 } 1780 } 1781 1782 leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Escloopdepth < modSrcLoopdepth 1783 1784 osrcesc = src.Esc 1785 switch src.Op { 1786 case ONAME: 1787 if src.Class == PPARAM && (leaks || dstE.Escloopdepth < 0) && src.Esc&EscMask < EscScope { 1788 if level.guaranteedDereference() > 0 { 1789 src.Esc = escMax(EscContentEscapes|src.Esc, EscNone) 1790 if Debug['m'] != 0 { 1791 if Debug['m'] <= 2 { 1792 if osrcesc != src.Esc { 1793 Warnl(src.Lineno, "leaking param content: %v", Nconv(src, FmtShort)) 1794 step.describe(src) 1795 } 1796 } else { 1797 Warnl(src.Lineno, "leaking param content: %v level=%v dst.eld=%v src.eld=%v dst=%v", 1798 Nconv(src, FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth, Nconv(dst, FmtShort)) 1799 } 1800 } 1801 } else { 1802 src.Esc = EscScope 1803 if Debug['m'] != 0 { 1804 1805 if Debug['m'] <= 2 { 1806 Warnl(src.Lineno, "leaking param: %v", Nconv(src, FmtShort)) 1807 step.describe(src) 1808 } else { 1809 Warnl(src.Lineno, "leaking param: %v level=%v dst.eld=%v src.eld=%v dst=%v", 1810 Nconv(src, FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth, Nconv(dst, FmtShort)) 1811 } 1812 } 1813 } 1814 } 1815 1816 // Treat a PPARAMREF closure variable as equivalent to the 1817 // original variable. 1818 if src.Class == PPARAMREF { 1819 if leaks && Debug['m'] != 0 { 1820 Warnl(src.Lineno, "leaking closure reference %v", Nconv(src, FmtShort)) 1821 step.describe(src) 1822 } 1823 escwalk(e, level, dst, src.Name.Param.Closure, e.stepWalk(dst, src.Name.Param.Closure, "closure-var", step)) 1824 } 1825 1826 case OPTRLIT, OADDR: 1827 why := "pointer literal" 1828 if src.Op == OADDR { 1829 why = "address-of" 1830 } 1831 if leaks { 1832 src.Esc = EscHeap 1833 addrescapes(src.Left) 1834 if Debug['m'] != 0 && osrcesc != src.Esc { 1835 p := src 1836 if p.Left.Op == OCLOSURE { 1837 p = p.Left // merely to satisfy error messages in tests 1838 } 1839 if Debug['m'] > 2 { 1840 Warnl(src.Lineno, "%v escapes to heap, level=%v, dst.eld=%v, src.eld=%v", 1841 Nconv(p, FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth) 1842 } else { 1843 Warnl(src.Lineno, "%v escapes to heap", Nconv(p, FmtShort)) 1844 step.describe(src) 1845 } 1846 } 1847 escwalkBody(e, level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step), modSrcLoopdepth) 1848 extraloopdepth = modSrcLoopdepth // passes to recursive case, seems likely a no-op 1849 } else { 1850 escwalk(e, level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step)) 1851 } 1852 1853 case OAPPEND: 1854 escwalk(e, level, dst, src.List.First(), e.stepWalk(dst, src.List.First(), "append-first-arg", step)) 1855 1856 case ODDDARG: 1857 if leaks { 1858 src.Esc = EscHeap 1859 if Debug['m'] != 0 && osrcesc != src.Esc { 1860 Warnl(src.Lineno, "%v escapes to heap", Nconv(src, FmtShort)) 1861 step.describe(src) 1862 } 1863 extraloopdepth = modSrcLoopdepth 1864 } 1865 // similar to a slice arraylit and its args. 1866 level = level.dec() 1867 1868 case OARRAYLIT: 1869 if src.Type.IsArray() { 1870 break 1871 } 1872 for _, n1 := range src.List.Slice() { 1873 escwalk(e, level.dec(), dst, n1.Right, e.stepWalk(dst, n1.Right, "slice-literal-element", step)) 1874 } 1875 1876 fallthrough 1877 1878 case OMAKECHAN, 1879 OMAKEMAP, 1880 OMAKESLICE, 1881 OARRAYRUNESTR, 1882 OARRAYBYTESTR, 1883 OSTRARRAYRUNE, 1884 OSTRARRAYBYTE, 1885 OADDSTR, 1886 OMAPLIT, 1887 ONEW, 1888 OCLOSURE, 1889 OCALLPART, 1890 ORUNESTR, 1891 OCONVIFACE: 1892 if leaks { 1893 src.Esc = EscHeap 1894 if Debug['m'] != 0 && osrcesc != src.Esc { 1895 Warnl(src.Lineno, "%v escapes to heap", Nconv(src, FmtShort)) 1896 step.describe(src) 1897 } 1898 extraloopdepth = modSrcLoopdepth 1899 } 1900 1901 case ODOT, 1902 ODOTTYPE: 1903 escwalk(e, level, dst, src.Left, e.stepWalk(dst, src.Left, "dot", step)) 1904 1905 case 1906 OSLICE, 1907 OSLICEARR, 1908 OSLICE3, 1909 OSLICE3ARR, 1910 OSLICESTR: 1911 escwalk(e, level, dst, src.Left, e.stepWalk(dst, src.Left, "slice", step)) 1912 1913 case OINDEX: 1914 if src.Left.Type.IsArray() { 1915 escwalk(e, level, dst, src.Left, e.stepWalk(dst, src.Left, "fixed-array-index-of", step)) 1916 break 1917 } 1918 fallthrough 1919 1920 case ODOTPTR: 1921 escwalk(e, level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "dot of pointer", step)) 1922 case OINDEXMAP: 1923 escwalk(e, level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "map index", step)) 1924 case OIND: 1925 escwalk(e, level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "indirection", step)) 1926 1927 // In this case a link went directly to a call, but should really go 1928 // to the dummy .outN outputs that were created for the call that 1929 // themselves link to the inputs with levels adjusted. 1930 // See e.g. #10466 1931 // This can only happen with functions returning a single result. 1932 case OCALLMETH, OCALLFUNC, OCALLINTER: 1933 if srcE.Escretval.Len() != 0 { 1934 if Debug['m'] > 2 { 1935 fmt.Printf("%v:[%d] dst %v escwalk replace src: %v with %v\n", 1936 linestr(lineno), e.loopdepth, 1937 Nconv(dst, FmtShort), Nconv(src, FmtShort), Nconv(srcE.Escretval.First(), FmtShort)) 1938 } 1939 src = srcE.Escretval.First() 1940 srcE = e.nodeEscState(src) 1941 } 1942 } 1943 1944 recurse: 1945 level = level.copy() 1946 for i, ll := range srcE.Escflowsrc { 1947 srcE.Escflowsrc[i].parent = step 1948 escwalkBody(e, level, dst, ll.src, &srcE.Escflowsrc[i], extraloopdepth) 1949 srcE.Escflowsrc[i].parent = nil 1950 } 1951 1952 e.pdepth-- 1953 } 1954 1955 // This special tag is applied to uintptr variables 1956 // that we believe may hold unsafe.Pointers for 1957 // calls into assembly functions. 1958 // It is logically a constant, but using a var 1959 // lets us take the address below to get a *string. 1960 var unsafeUintptrTag = "unsafe-uintptr" 1961 1962 func esctag(e *EscState, func_ *Node) { 1963 func_.Esc = EscFuncTagged 1964 1965 // External functions are assumed unsafe, 1966 // unless //go:noescape is given before the declaration. 1967 if len(func_.Nbody.Slice()) == 0 { 1968 if func_.Noescape { 1969 for _, t := range func_.Type.Params().Fields().Slice() { 1970 if haspointers(t.Type) { 1971 t.Note = mktag(EscNone) 1972 } 1973 } 1974 } 1975 1976 // Assume that uintptr arguments must be held live across the call. 1977 // This is most important for syscall.Syscall. 1978 // See golang.org/issue/13372. 1979 // This really doesn't have much to do with escape analysis per se, 1980 // but we are reusing the ability to annotate an individual function 1981 // argument and pass those annotations along to importing code. 1982 narg := 0 1983 for _, t := range func_.Type.Params().Fields().Slice() { 1984 narg++ 1985 if t.Type.Etype == TUINTPTR { 1986 if Debug['m'] != 0 { 1987 var name string 1988 if t.Sym != nil { 1989 name = t.Sym.Name 1990 } else { 1991 name = fmt.Sprintf("arg#%d", narg) 1992 } 1993 Warnl(func_.Lineno, "%v assuming %v is unsafe uintptr", funcSym(func_), name) 1994 } 1995 t.Note = &unsafeUintptrTag 1996 } 1997 } 1998 1999 return 2000 } 2001 2002 savefn := Curfn 2003 Curfn = func_ 2004 2005 for _, ln := range Curfn.Func.Dcl { 2006 if ln.Op != ONAME { 2007 continue 2008 } 2009 2010 switch ln.Esc & EscMask { 2011 case EscNone, // not touched by escflood 2012 EscReturn: 2013 if haspointers(ln.Type) { // don't bother tagging for scalars 2014 ln.Name.Param.Field.Note = mktag(int(ln.Esc)) 2015 } 2016 2017 case EscHeap, // touched by escflood, moved to heap 2018 EscScope: // touched by escflood, value leaves scope 2019 break 2020 } 2021 } 2022 2023 Curfn = savefn 2024 }