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