diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index d03a97ef33..2cb4510332 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -14,11 +14,11 @@ import ( // or single non-recursive functions, bottom up. // // Finding these sets is finding strongly connected components -// in the static call graph. The algorithm for doing that is taken -// from Sedgewick, Algorithms, Second Edition, p. 482, with two -// adaptations. +// by reverse topological order in the static call graph. +// The algorithm (known as Tarjan's algorithm) for doing that is taken from +// Sedgewick, Algorithms, Second Edition, p. 482, with two adaptations. // -// First, a hidden closure function (n->curfn != N) cannot be the +// First, a hidden closure function (n.Func.FCurfn != nil) cannot be the // root of a connected component. Refusing to use it as a root // forces it into the component of the function in which it appears. // This is more convenient for escape analysis. @@ -81,8 +81,8 @@ func (v *bottomUpVisitor) visit(n *Node) uint32 { if (min == id || min == id+1) && n.Func.FCurfn == nil { // This node is the root of a strongly connected component. - // The original min passed to visitcodelist was n->walkgen+1. - // If visitcodelist found its way back to n->walkgen, then this + // The original min passed to visitcodelist was v.nodeID[n]+1. + // If visitcodelist found its way back to v.nodeID[n], then this // block is a set of mutually recursive functions. // Otherwise it's just a lone function that does not recurse. recursive := min == id @@ -160,7 +160,7 @@ func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 { // // First escfunc, esc and escassign recurse over the ast of each // function to dig out flow(dst,src) edges between any -// pointer-containing nodes and store them in dst->escflowsrc. For +// pointer-containing nodes and store them in e.nodeEscState(dst).Flowsrc. For // variables assigned to a variable in an outer scope or used as a // return value, they store a flow(theSink, src) edge to a fake node // 'the Sink'. For variables referenced in closures, an edge @@ -309,10 +309,10 @@ type EscStep struct { type NodeEscState struct { Curfn *Node - Escflowsrc []EscStep // flow(this, src) - Escretval Nodes // on OCALLxxx, list of dummy return values - Escloopdepth int32 // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes - Esclevel Level + Flowsrc []EscStep // flow(this, src) + Retval Nodes // on OCALLxxx, list of dummy return values + Loopdepth int32 // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes + Level Level Walkgen uint32 Maxextraloopdepth int32 } @@ -324,8 +324,9 @@ func (e *EscState) nodeEscState(n *Node) *NodeEscState { if n.Opt() != nil { Fatalf("nodeEscState: opt in use (%T)", n.Opt()) } - nE := new(NodeEscState) - nE.Curfn = Curfn + nE := &NodeEscState{ + Curfn: Curfn, + } n.SetOpt(nE) e.opts = append(e.opts, n) return nE @@ -337,7 +338,7 @@ func (e *EscState) track(n *Node) { } n.Esc = EscNone // until proven otherwise nE := e.nodeEscState(n) - nE.Escloopdepth = e.loopdepth + nE.Loopdepth = e.loopdepth e.noesc = append(e.noesc, n) } @@ -409,6 +410,17 @@ type EscState struct { walkgen uint32 } +func newEscState(recursive bool) *EscState { + e := new(EscState) + e.theSink.Op = ONAME + e.theSink.Orig = &e.theSink + e.theSink.Class = PEXTERN + e.theSink.Sym = lookup(".sink") + e.nodeEscState(&e.theSink).Loopdepth = -1 + e.recursive = recursive + return e +} + func (e *EscState) stepWalk(dst, src *Node, why string, parent *EscStep) *EscStep { // TODO: keep a cache of these, mark entry/exit in escwalk to avoid allocation // Or perhaps never mind, since it is disabled unless printing is on. @@ -446,14 +458,7 @@ func (e *EscState) curfnSym(n *Node) *Sym { } func escAnalyze(all []*Node, recursive bool) { - var es EscState - e := &es - e.theSink.Op = ONAME - e.theSink.Orig = &e.theSink - e.theSink.Class = PEXTERN - e.theSink.Sym = lookup(".sink") - e.nodeEscState(&e.theSink).Escloopdepth = -1 - e.recursive = recursive + e := newEscState(recursive) for _, n := range all { if n.Op == ODCLFUNC { @@ -464,11 +469,11 @@ func escAnalyze(all []*Node, recursive bool) { // flow-analyze functions for _, n := range all { if n.Op == ODCLFUNC { - escfunc(e, n) + e.escfunc(n) } } - // print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount); + // print("escapes: %d e.dsts, %d edges\n", e.dstcount, e.edgecount); // visit the upstream of each dst, mark address nodes with // addrescapes, mark parameters unsafe @@ -477,7 +482,7 @@ func escAnalyze(all []*Node, recursive bool) { escapes[i] = n.Esc } for _, n := range e.dsts { - escflood(e, n) + e.escflood(n) } for { done := true @@ -488,7 +493,7 @@ func escAnalyze(all []*Node, recursive bool) { Warnl(n.Lineno, "Reflooding %v %S", e.curfnSym(n), n) } escapes[i] = n.Esc - escflood(e, n) + e.escflood(n) } } if done { @@ -499,7 +504,7 @@ func escAnalyze(all []*Node, recursive bool) { // for all top level functions, tag the typenodes corresponding to the param nodes for _, n := range all { if n.Op == ODCLFUNC { - esctag(e, n) + e.esctag(n) } } @@ -510,35 +515,36 @@ func escAnalyze(all []*Node, recursive bool) { } } } + for _, x := range e.opts { x.SetOpt(nil) } } -func escfunc(e *EscState, func_ *Node) { - // print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":""); - if func_.Esc != 1 { - Fatalf("repeat escfunc %v", func_.Func.Nname) +func (e *EscState) escfunc(fn *Node) { + // print("escfunc %N %s\n", fn.Func.Nname, e.recursive?"(recursive)":""); + if fn.Esc != EscFuncPlanned { + Fatalf("repeat escfunc %v", fn.Func.Nname) } - func_.Esc = EscFuncStarted + fn.Esc = EscFuncStarted saveld := e.loopdepth e.loopdepth = 1 savefn := Curfn - Curfn = func_ + Curfn = fn for _, ln := range Curfn.Func.Dcl { if ln.Op != ONAME { continue } - llNE := e.nodeEscState(ln) + lnE := e.nodeEscState(ln) switch ln.Class { // out params are in a loopdepth between the sink and all local variables case PPARAMOUT: - llNE.Escloopdepth = 0 + lnE.Loopdepth = 0 case PPARAM: - llNE.Escloopdepth = 1 + lnE.Loopdepth = 1 if ln.Type != nil && !haspointers(ln.Type) { break } @@ -555,36 +561,36 @@ func escfunc(e *EscState, func_ *Node) { if e.recursive { for _, ln := range Curfn.Func.Dcl { if ln.Op == ONAME && ln.Class == PPARAMOUT { - escflows(e, &e.theSink, ln, e.stepAssign(nil, ln, ln, "returned from recursive function")) + e.escflows(&e.theSink, ln, e.stepAssign(nil, ln, ln, "returned from recursive function")) } } } - escloopdepthlist(e, Curfn.Nbody) - esclist(e, Curfn.Nbody, Curfn) + e.escloopdepthlist(Curfn.Nbody) + e.esclist(Curfn.Nbody, Curfn) Curfn = savefn e.loopdepth = saveld } -// Mark labels that have no backjumps to them as not increasing e->loopdepth. -// Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat +// Mark labels that have no backjumps to them as not increasing e.loopdepth. +// Walk hasn't generated (goto|label).Left.Sym.Label yet, so we'll cheat // and set it to one of the following two. Then in esc we'll clear it again. var looping Label var nonlooping Label -func escloopdepthlist(e *EscState, l Nodes) { +func (e *EscState) escloopdepthlist(l Nodes) { for _, n := range l.Slice() { - escloopdepth(e, n) + e.escloopdepth(n) } } -func escloopdepth(e *EscState, n *Node) { +func (e *EscState) escloopdepth(n *Node) { if n == nil { return } - escloopdepthlist(e, n.Ninit) + e.escloopdepthlist(n.Ninit) switch n.Op { case OLABEL: @@ -594,7 +600,7 @@ func escloopdepth(e *EscState, n *Node) { // Walk will complain about this label being already defined, but that's not until // after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc - // if(n->left->sym->label != nil) + // if(n.Left.Sym.Label != nil) // fatal("escape analysis messed up analyzing label: %+N", n); n.Left.Sym.Label = &nonlooping @@ -610,20 +616,20 @@ func escloopdepth(e *EscState, n *Node) { } } - escloopdepth(e, n.Left) - escloopdepth(e, n.Right) - escloopdepthlist(e, n.List) - escloopdepthlist(e, n.Nbody) - escloopdepthlist(e, n.Rlist) + e.escloopdepth(n.Left) + e.escloopdepth(n.Right) + e.escloopdepthlist(n.List) + e.escloopdepthlist(n.Nbody) + e.escloopdepthlist(n.Rlist) } -func esclist(e *EscState, l Nodes, up *Node) { +func (e *EscState) esclist(l Nodes, parent *Node) { for _, n := range l.Slice() { - esc(e, n, up) + e.esc(n, parent) } } -func esc(e *EscState, n *Node, up *Node) { +func (e *EscState) esc(n *Node, parent *Node) { if n == nil { return } @@ -637,7 +643,7 @@ func esc(e *EscState, n *Node, up *Node) { lno := setlineno(n) // ninit logically runs at a different loopdepth than the rest of the for loop. - esclist(e, n.Ninit, n) + e.esclist(n.Ninit, n) if n.Op == OFOR || n.Op == ORANGE { e.loopdepth++ @@ -651,7 +657,7 @@ func esc(e *EscState, n *Node, up *Node) { for _, n1 := range n.List.Slice() { // cases // it.N().Rlist is the variable per case if n1.Rlist.Len() != 0 { - e.nodeEscState(n1.Rlist.First()).Escloopdepth = e.loopdepth + e.nodeEscState(n1.Rlist.First()).Loopdepth = e.loopdepth } } } @@ -667,14 +673,14 @@ func esc(e *EscState, n *Node, up *Node) { } n.Esc = EscHeap addrescapes(n) - escassignSinkNilWhy(e, n, n, "too large for stack") // TODO category: tooLarge + e.escassignSinkNilWhy(n, n, "too large for stack") // TODO category: tooLarge } - esc(e, n.Left, n) - esc(e, n.Right, n) - esclist(e, n.Nbody, n) - esclist(e, n.List, n) - esclist(e, n.Rlist, n) + e.esc(n.Left, n) + e.esc(n.Right, n) + e.esclist(n.Nbody, n) + e.esclist(n.List, n) + e.esclist(n.Rlist, n) if n.Op == OFOR || n.Op == ORANGE { e.loopdepth-- @@ -688,7 +694,7 @@ func esc(e *EscState, n *Node, up *Node) { // Record loop depth at declaration. case ODCL: if n.Left != nil { - e.nodeEscState(n.Left).Escloopdepth = e.loopdepth + e.nodeEscState(n.Left).Loopdepth = e.loopdepth } case OLABEL: @@ -704,7 +710,7 @@ func esc(e *EscState, n *Node, up *Node) { } // See case OLABEL in escloopdepth above - // else if(n->left->sym->label == nil) + // else if(n.Left.Sym.Label == nil) // fatal("escape analysis missed or messed up a label: %+N", n); n.Left.Sym.Label = nil @@ -718,9 +724,9 @@ func esc(e *EscState, n *Node, up *Node) { // dereferenced (see #12588) if n.Type.IsArray() && !(n.Right.Type.IsPtr() && eqtype(n.Right.Type.Elem(), n.Type)) { - escassignNilWhy(e, n.List.Second(), n.Right, "range") + e.escassignNilWhy(n.List.Second(), n.Right, "range") } else { - escassignDereference(e, n.List.Second(), n.Right, e.stepAssign(nil, n.List.Second(), n.Right, "range-deref")) + e.escassignDereference(n.List.Second(), n.Right, e.stepAssign(nil, n.List.Second(), n.Right, "range-deref")) } } @@ -731,7 +737,7 @@ func esc(e *EscState, n *Node, up *Node) { // n.Left.Right is the argument of the .(type), // it.N().Rlist is the variable per case if n2.Rlist.Len() != 0 { - escassignNilWhy(e, n2.Rlist.First(), n.Left.Right, "switch case") + e.escassignNilWhy(n2.Rlist.First(), n.Left.Right, "switch case") } } } @@ -769,25 +775,25 @@ func esc(e *EscState, n *Node, up *Node) { break } - escassign(e, n.Left, n.Right, nil) + e.escassign(n.Left, n.Right, nil) case OAS2: // x,y = a,b if n.List.Len() == n.Rlist.Len() { rs := n.Rlist.Slice() for i, n := range n.List.Slice() { - escassignNilWhy(e, n, rs[i], "assign-pair") + e.escassignNilWhy(n, rs[i], "assign-pair") } } case OAS2RECV: // v, ok = <-ch - escassignNilWhy(e, n.List.First(), n.Rlist.First(), "assign-pair-receive") + e.escassignNilWhy(n.List.First(), n.Rlist.First(), "assign-pair-receive") case OAS2MAPR: // v, ok = m[k] - escassignNilWhy(e, n.List.First(), n.Rlist.First(), "assign-pair-mapr") + e.escassignNilWhy(n.List.First(), n.Rlist.First(), "assign-pair-mapr") case OAS2DOTTYPE: // v, ok = x.(type) - escassignNilWhy(e, n.List.First(), n.Rlist.First(), "assign-pair-dot-type") + e.escassignNilWhy(n.List.First(), n.Rlist.First(), "assign-pair-dot-type") case OSEND: // ch <- x - escassignSinkNilWhy(e, n, n.Right, "send") + e.escassignSinkNilWhy(n, n.Right, "send") case ODEFER: if e.loopdepth == 1 { // top level @@ -796,96 +802,96 @@ func esc(e *EscState, n *Node, up *Node) { // arguments leak out of scope // TODO: leak to a dummy node instead // defer f(x) - f and x escape - escassignSinkNilWhy(e, n, n.Left.Left, "defer func") + e.escassignSinkNilWhy(n, n.Left.Left, "defer func") - escassignSinkNilWhy(e, n, n.Left.Right, "defer func ...") // ODDDARG for call + e.escassignSinkNilWhy(n, n.Left.Right, "defer func ...") // ODDDARG for call for _, n4 := range n.Left.List.Slice() { - escassignSinkNilWhy(e, n, n4, "defer func arg") + e.escassignSinkNilWhy(n, n4, "defer func arg") } case OPROC: // go f(x) - f and x escape - escassignSinkNilWhy(e, n, n.Left.Left, "go func") + e.escassignSinkNilWhy(n, n.Left.Left, "go func") - escassignSinkNilWhy(e, n, n.Left.Right, "go func ...") // ODDDARG for call + e.escassignSinkNilWhy(n, n.Left.Right, "go func ...") // ODDDARG for call for _, n4 := range n.Left.List.Slice() { - escassignSinkNilWhy(e, n, n4, "go func arg") + e.escassignSinkNilWhy(n, n4, "go func arg") } case OCALLMETH, OCALLFUNC, OCALLINTER: - esccall(e, n, up) + e.esccall(n, parent) - // esccall already done on n->rlist->n. tie it's escretval to n->list + // esccall already done on n.Rlist.First(). tie it's Retval to n.List case OAS2FUNC: // x,y = f() - rs := e.nodeEscState(n.Rlist.First()).Escretval.Slice() + rs := e.nodeEscState(n.Rlist.First()).Retval.Slice() for i, n := range n.List.Slice() { if i >= len(rs) { break } - escassignNilWhy(e, n, rs[i], "assign-pair-func-call") + e.escassignNilWhy(n, rs[i], "assign-pair-func-call") } if n.List.Len() != len(rs) { Fatalf("esc oas2func") } case ORETURN: - ll := n.List - if n.List.Len() == 1 && Curfn.Type.Results().NumFields() > 1 { + retList := n.List + if retList.Len() == 1 && Curfn.Type.Results().NumFields() > 1 { // OAS2FUNC in disguise - // esccall already done on n->list->n - // tie n->list->n->escretval to curfn->dcl PPARAMOUT's - ll = e.nodeEscState(n.List.First()).Escretval + // esccall already done on n.List.First() + // tie e.nodeEscState(n.List.First()).Retval to Curfn.Func.Dcl PPARAMOUT's + retList = e.nodeEscState(n.List.First()).Retval } i := 0 for _, lrn := range Curfn.Func.Dcl { - if i >= ll.Len() { + if i >= retList.Len() { break } if lrn.Op != ONAME || lrn.Class != PPARAMOUT { continue } - escassignNilWhy(e, lrn, ll.Index(i), "return") + e.escassignNilWhy(lrn, retList.Index(i), "return") i++ } - if i < ll.Len() { + if i < retList.Len() { Fatalf("esc return list") } // Argument could leak through recover. case OPANIC: - escassignSinkNilWhy(e, n, n.Left, "panic") + e.escassignSinkNilWhy(n, n.Left, "panic") case OAPPEND: if !n.Isddd { for _, nn := range n.List.Slice()[1:] { - escassignSinkNilWhy(e, n, nn, "appended to slice") // lose track of assign to dereference + e.escassignSinkNilWhy(n, nn, "appended to slice") // lose track of assign to dereference } } else { // append(slice1, slice2...) -- slice2 itself does not escape, but contents do. slice2 := n.List.Second() - escassignDereference(e, &e.theSink, slice2, e.stepAssign(nil, n, slice2, "appended slice...")) // lose track of assign of dereference + e.escassignDereference(&e.theSink, slice2, e.stepAssign(nil, n, slice2, "appended slice...")) // lose track of assign of dereference if Debug['m'] > 3 { Warnl(n.Lineno, "%v special treatment of append(slice1, slice2...) %S", e.curfnSym(n), n) } } - escassignDereference(e, &e.theSink, n.List.First(), e.stepAssign(nil, n, n.List.First(), "appendee slice")) // The original elements are now leaked, too + e.escassignDereference(&e.theSink, n.List.First(), e.stepAssign(nil, n, n.List.First(), "appendee slice")) // The original elements are now leaked, too case OCOPY: - escassignDereference(e, &e.theSink, n.Right, e.stepAssign(nil, n, n.Right, "copied slice")) // lose track of assign of dereference + e.escassignDereference(&e.theSink, n.Right, e.stepAssign(nil, n, n.Right, "copied slice")) // lose track of assign of dereference case OCONV, OCONVNOP: - escassignNilWhy(e, n, n.Left, "converted") + e.escassignNilWhy(n, n.Left, "converted") case OCONVIFACE: e.track(n) - escassignNilWhy(e, n, n.Left, "interface-converted") + e.escassignNilWhy(n, n.Left, "interface-converted") case OARRAYLIT: // Link values to array for _, n5 := range n.List.Slice() { - escassign(e, n, n5.Right, e.stepAssign(nil, n, n5.Right, "array literal element")) + e.escassign(n, n5.Right, e.stepAssign(nil, n, n5.Right, "array literal element")) } case OSLICELIT: @@ -893,33 +899,33 @@ func esc(e *EscState, n *Node, up *Node) { e.track(n) // Link values to slice for _, n5 := range n.List.Slice() { - escassign(e, n, n5.Right, e.stepAssign(nil, n, n5.Right, "slice literal element")) + e.escassign(n, n5.Right, e.stepAssign(nil, n, n5.Right, "slice literal element")) } // Link values to struct. case OSTRUCTLIT: for _, n6 := range n.List.Slice() { - escassignNilWhy(e, n, n6.Right, "struct literal element") + e.escassignNilWhy(n, n6.Right, "struct literal element") } case OPTRLIT: e.track(n) // Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too. - escassignNilWhy(e, n, n.Left, "pointer literal [assign]") + e.escassignNilWhy(n, n.Left, "pointer literal [assign]") case OCALLPART: e.track(n) // Contents make it to memory, lose track. - escassignSinkNilWhy(e, n, n.Left, "call part") + e.escassignSinkNilWhy(n, n.Left, "call part") case OMAPLIT: e.track(n) // Keys and values make it to memory, lose track. for _, n7 := range n.List.Slice() { - escassignSinkNilWhy(e, n, n7.Left, "map literal key") - escassignSinkNilWhy(e, n, n7.Right, "map literal value") + e.escassignSinkNilWhy(n, n7.Left, "map literal key") + e.escassignSinkNilWhy(n, n7.Right, "map literal value") } case OCLOSURE: @@ -932,11 +938,11 @@ func esc(e *EscState, n *Node, up *Node) { if !v.Name.Byval { a = nod(OADDR, a, nil) a.Lineno = v.Lineno - e.nodeEscState(a).Escloopdepth = e.loopdepth + e.nodeEscState(a).Loopdepth = e.loopdepth a = typecheck(a, Erv) } - escassignNilWhy(e, n, a, "captured by a closure") + e.escassignNilWhy(n, a, "captured by a closure") } fallthrough @@ -968,8 +974,8 @@ func esc(e *EscState, n *Node, up *Node) { case PAUTO: nE := e.nodeEscState(n) leftE := e.nodeEscState(n.Left) - if leftE.Escloopdepth != 0 { - nE.Escloopdepth = leftE.Escloopdepth + if leftE.Loopdepth != 0 { + nE.Loopdepth = leftE.Loopdepth } // PPARAM is loop depth 1 always. @@ -980,7 +986,7 @@ func esc(e *EscState, n *Node, up *Node) { // first result move to the heap. case PPARAM, PPARAMOUT: nE := e.nodeEscState(n) - nE.Escloopdepth = 1 + nE.Loopdepth = 1 } } } @@ -990,29 +996,29 @@ func esc(e *EscState, n *Node, up *Node) { // escassignNilWhy bundles a common case of // escassign(e, dst, src, e.stepAssign(nil, dst, src, reason)) -func escassignNilWhy(e *EscState, dst, src *Node, reason string) { +func (e *EscState) escassignNilWhy(dst, src *Node, reason string) { var step *EscStep if Debug['m'] != 0 { step = e.stepAssign(nil, dst, src, reason) } - escassign(e, dst, src, step) + e.escassign(dst, src, step) } // escassignSinkNilWhy bundles a common case of // escassign(e, &e.theSink, src, e.stepAssign(nil, dst, src, reason)) -func escassignSinkNilWhy(e *EscState, dst, src *Node, reason string) { +func (e *EscState) escassignSinkNilWhy(dst, src *Node, reason string) { var step *EscStep if Debug['m'] != 0 { step = e.stepAssign(nil, dst, src, reason) } - escassign(e, &e.theSink, src, step) + e.escassign(&e.theSink, src, step) } // Assert that expr somehow gets assigned to dst, if non nil. for // dst==nil, any name node expr still must be marked as being // evaluated in curfn. For expr==nil, dst must still be examined for // evaluations inside it (e.g *f(x) = y) -func escassign(e *EscState, dst, src *Node, step *EscStep) { +func (e *EscState) escassign(dst, src *Node, step *EscStep) { if isblank(dst) || dst == nil || src == nil || src.Op == ONONAME || src.Op == OXXX { return } @@ -1030,7 +1036,7 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) { dstwhy := "assigned" // Analyze lhs of assignment. - // Replace dst with e->theSink if we can't track it. + // Replace dst with &e.theSink if we can't track it. switch dst.Op { default: Dump("dst", dst) @@ -1047,7 +1053,6 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) { OPTRLIT, ODDDARG, OCALLPART: - break case ONAME: if dst.Class == PEXTERN { @@ -1056,12 +1061,12 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) { } case ODOT: // treat "dst.x = src" as "dst = src" - escassign(e, dst.Left, src, e.stepAssign(step, originalDst, src, "dot-equals")) + e.escassign(dst.Left, src, e.stepAssign(step, originalDst, src, "dot-equals")) return case OINDEX: if dst.Left.Type.IsArray() { - escassign(e, dst.Left, src, e.stepAssign(step, originalDst, src, "array-element-equals")) + e.escassign(dst.Left, src, e.stepAssign(step, originalDst, src, "array-element-equals")) return } @@ -1078,7 +1083,7 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) { // lose track of key and value case OINDEXMAP: - escassign(e, &e.theSink, dst.Right, e.stepAssign(nil, originalDst, src, "key of map put")) + e.escassign(&e.theSink, dst.Right, e.stepAssign(nil, originalDst, src, "key of map put")) dstwhy = "value of map put" dst = &e.theSink } @@ -1109,22 +1114,22 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) { OCALLPART, ORUNESTR, OCONVIFACE: - escflows(e, dst, src, e.stepAssign(step, originalDst, src, dstwhy)) + e.escflows(dst, src, e.stepAssign(step, originalDst, src, dstwhy)) case OCLOSURE: // OCLOSURE is lowered to OPTRLIT, // insert OADDR to account for the additional indirection. a := nod(OADDR, src, nil) a.Lineno = src.Lineno - e.nodeEscState(a).Escloopdepth = e.nodeEscState(src).Escloopdepth + e.nodeEscState(a).Loopdepth = e.nodeEscState(src).Loopdepth a.Type = ptrto(src.Type) - escflows(e, dst, a, e.stepAssign(nil, originalDst, src, dstwhy)) + e.escflows(dst, a, e.stepAssign(nil, originalDst, src, dstwhy)) // Flowing multiple returns to a single dst happens when // analyzing "go f(g())": here g() flows to sink (issue 4529). case OCALLMETH, OCALLFUNC, OCALLINTER: - for _, n := range e.nodeEscState(src).Escretval.Slice() { - escflows(e, dst, n, e.stepAssign(nil, originalDst, n, dstwhy)) + for _, n := range e.nodeEscState(src).Retval.Slice() { + e.escflows(dst, n, e.stepAssign(nil, originalDst, n, dstwhy)) } // A non-pointer escaping from a struct does not concern us. @@ -1146,26 +1151,26 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) { OSLICE3ARR, OSLICESTR: // Conversions, field access, slice all preserve the input value. - escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) + e.escassign(dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) case ODOTTYPE, ODOTTYPE2: if src.Type != nil && !haspointers(src.Type) { break } - escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) + e.escassign(dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) case OAPPEND: // Append returns first argument. // Subsequent arguments are already leaked because they are operands to append. - escassign(e, dst, src.List.First(), e.stepAssign(step, dst, src.List.First(), dstwhy)) + e.escassign(dst, src.List.First(), e.stepAssign(step, dst, src.List.First(), dstwhy)) case OINDEX: // Index of array preserves input value. if src.Left.Type.IsArray() { - escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) + e.escassign(dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) } else { - escflows(e, dst, src, e.stepAssign(step, originalDst, src, dstwhy)) + e.escflows(dst, src, e.stepAssign(step, originalDst, src, dstwhy)) } // Might be pointer arithmetic, in which case @@ -1185,9 +1190,9 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) { OPLUS, OMINUS, OCOM: - escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) + e.escassign(dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy)) - escassign(e, dst, src.Right, e.stepAssign(step, originalDst, src, dstwhy)) + e.escassign(dst, src.Right, e.stepAssign(step, originalDst, src, dstwhy)) } e.pdepth-- @@ -1207,8 +1212,6 @@ var tags [1 << (bitsPerOutputInTag + EscReturnBits)]string func mktag(mask int) string { switch mask & EscMask { case EscNone, EscReturn: - break - default: Fatalf("escape mktag") } @@ -1291,7 +1294,7 @@ func describeEscape(em uint16) string { // escassignfromtag models the input-to-output assignment flow of one of a function // calls arguments, where the flow is encoded in "note". -func escassignfromtag(e *EscState, note string, dsts Nodes, src *Node) uint16 { +func (e *EscState) escassignfromtag(note string, dsts Nodes, src *Node) uint16 { em := parsetag(note) if src.Op == OLITERAL { return em @@ -1303,7 +1306,7 @@ func escassignfromtag(e *EscState, note string, dsts Nodes, src *Node) uint16 { } if em == EscUnknown { - escassignSinkNilWhy(e, src, src, "passed to function[unknown]") + e.escassignSinkNilWhy(src, src, "passed to function[unknown]") return em } @@ -1314,7 +1317,7 @@ func escassignfromtag(e *EscState, note string, dsts Nodes, src *Node) uint16 { // If content inside parameter (reached via indirection) // escapes to heap, mark as such. if em&EscContentEscapes != 0 { - escassign(e, &e.theSink, e.addDereference(src), e.stepAssign(nil, src, src, "passed to function[content escapes]")) + e.escassign(&e.theSink, e.addDereference(src), e.stepAssign(nil, src, src, "passed to function[content escapes]")) } em0 := em @@ -1331,7 +1334,7 @@ func escassignfromtag(e *EscState, note string, dsts Nodes, src *Node) uint16 { for i := uint16(0); i < embits-1; i++ { n = e.addDereference(n) // encode level>0 as indirections } - escassign(e, dsts.Index(dstsi), n, e.stepAssign(nil, dsts.Index(dstsi), src, "passed-to-and-returned-from-function")) + e.escassign(dsts.Index(dstsi), n, e.stepAssign(nil, dsts.Index(dstsi), src, "passed-to-and-returned-from-function")) } dstsi++ } @@ -1345,11 +1348,11 @@ func escassignfromtag(e *EscState, note string, dsts Nodes, src *Node) uint16 { return em0 } -func escassignDereference(e *EscState, dst *Node, src *Node, step *EscStep) { +func (e *EscState) escassignDereference(dst *Node, src *Node, step *EscStep) { if src.Op == OLITERAL { return } - escassign(e, dst, e.addDereference(src), step) + e.escassign(dst, e.addDereference(src), step) } // addDereference constructs a suitable OIND note applied to src. @@ -1357,7 +1360,7 @@ func escassignDereference(e *EscState, dst *Node, src *Node, step *EscStep) { // some semantically dubious node combinations are (currently) possible. func (e *EscState) addDereference(n *Node) *Node { ind := nod(OIND, n, nil) - e.nodeEscState(ind).Escloopdepth = e.nodeEscState(n).Escloopdepth + e.nodeEscState(ind).Loopdepth = e.nodeEscState(n).Loopdepth ind.Lineno = n.Lineno t := n.Type if t.IsKind(Tptr) { @@ -1404,147 +1407,145 @@ func escNoteOutputParamFlow(e uint16, vargen int32, level Level) uint16 { return (e &^ (bitsMaskForTag << shift)) | encodedFlow } -func initEscretval(e *EscState, n *Node, fntype *Type) { - i := 0 - nE := e.nodeEscState(n) - nE.Escretval.Set(nil) // Suspect this is not nil for indirect calls. - for _, t := range fntype.Results().Fields().Slice() { - src := nod(ONAME, nil, nil) +func (e *EscState) initEscRetval(call *Node, fntype *Type) { + cE := e.nodeEscState(call) + cE.Retval.Set(nil) // Suspect this is not nil for indirect calls. + for i, f := range fntype.Results().Fields().Slice() { + ret := nod(ONAME, nil, nil) buf := fmt.Sprintf(".out%d", i) - i++ - src.Sym = lookup(buf) - src.Type = t.Type - src.Class = PAUTO - src.Name.Curfn = Curfn - e.nodeEscState(src).Escloopdepth = e.loopdepth - src.Used = true - src.Lineno = n.Lineno - nE.Escretval.Append(src) + ret.Sym = lookup(buf) + ret.Type = f.Type + ret.Class = PAUTO + ret.Name.Curfn = Curfn + e.nodeEscState(ret).Loopdepth = e.loopdepth + ret.Used = true + ret.Lineno = call.Lineno + cE.Retval.Append(ret) } } // This is a bit messier than fortunate, pulled out of esc's big -// switch for clarity. We either have the paramnodes, which may be +// switch for clarity. We either have the paramnodes, which may be // connected to other things through flows or we have the parameter type // nodes, which may be marked "noescape". Navigating the ast is slightly // different for methods vs plain functions and for imported vs // this-package -func esccall(e *EscState, n *Node, up *Node) { +func (e *EscState) esccall(call *Node, parent *Node) { var fntype *Type var indirect bool var fn *Node - switch n.Op { + switch call.Op { default: Fatalf("esccall") case OCALLFUNC: - fn = n.Left + fn = call.Left fntype = fn.Type indirect = fn.Op != ONAME || fn.Class != PFUNC case OCALLMETH: - fn = n.Left.Sym.Def + fn = call.Left.Sym.Def if fn != nil { fntype = fn.Type } else { - fntype = n.Left.Type + fntype = call.Left.Type } case OCALLINTER: - fntype = n.Left.Type + fntype = call.Left.Type indirect = true } - ll := n.List - if n.List.Len() == 1 { - a := n.List.First() - if a.Type.IsFuncArgStruct() { // f(g()) - ll = e.nodeEscState(a).Escretval + argList := call.List + if argList.Len() == 1 { + arg := argList.First() + if arg.Type.IsFuncArgStruct() { // f(g()) + argList = e.nodeEscState(arg).Retval } } + args := argList.Slice() + if indirect { // We know nothing! // Leak all the parameters - for _, n1 := range ll.Slice() { - escassignSinkNilWhy(e, n, n1, "parameter to indirect call") + for _, arg := range args { + e.escassignSinkNilWhy(call, arg, "parameter to indirect call") if Debug['m'] > 3 { - fmt.Printf("%v::esccall:: indirect call <- %S, untracked\n", linestr(lineno), n1) + fmt.Printf("%v::esccall:: indirect call <- %S, untracked\n", linestr(lineno), arg) } } // Set up bogus outputs - initEscretval(e, n, fntype) + e.initEscRetval(call, fntype) // If there is a receiver, it also leaks to heap. - if n.Op != OCALLFUNC { - t := fntype.Recv() - src := n.Left.Left - if haspointers(t.Type) { - escassignSinkNilWhy(e, n, src, "receiver in indirect call") + if call.Op != OCALLFUNC { + rf := fntype.Recv() + r := call.Left.Left + if haspointers(rf.Type) { + e.escassignSinkNilWhy(call, r, "receiver in indirect call") } } else { // indirect and OCALLFUNC = could be captured variables, too. (#14409) - ll := e.nodeEscState(n).Escretval.Slice() - for _, llN := range ll { - escassignDereference(e, llN, fn, e.stepAssign(nil, llN, fn, "captured by called closure")) + rets := e.nodeEscState(call).Retval.Slice() + for _, ret := range rets { + e.escassignDereference(ret, fn, e.stepAssign(nil, ret, fn, "captured by called closure")) } } return } - nE := e.nodeEscState(n) + cE := e.nodeEscState(call) if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Name.Defn != nil && fn.Name.Defn.Nbody.Len() != 0 && fn.Name.Param.Ntype != nil && fn.Name.Defn.Esc < EscFuncTagged { if Debug['m'] > 3 { - fmt.Printf("%v::esccall:: %S in recursive group\n", linestr(lineno), n) + fmt.Printf("%v::esccall:: %S in recursive group\n", linestr(lineno), call) } // function in same mutually recursive group. Incorporate into flow graph. - // print("esc local fn: %N\n", fn->ntype); - if fn.Name.Defn.Esc == EscFuncUnknown || nE.Escretval.Len() != 0 { + // print("esc local fn: %N\n", fn.Func.Ntype); + if fn.Name.Defn.Esc == EscFuncUnknown || cE.Retval.Len() != 0 { Fatalf("graph inconsistency") } - lls := ll.Slice() sawRcvr := false - var src *Node - for _, n2 := range fn.Name.Defn.Func.Dcl { - switch n2.Class { + for _, n := range fn.Name.Defn.Func.Dcl { + switch n.Class { case PPARAM: - if n.Op != OCALLFUNC && !sawRcvr { - escassignNilWhy(e, n2, n.Left.Left, "call receiver") + if call.Op != OCALLFUNC && !sawRcvr { + e.escassignNilWhy(n, call.Left.Left, "call receiver") sawRcvr = true continue } - if len(lls) == 0 { + if len(args) == 0 { continue } - src = lls[0] - if n2.Isddd && !n.Isddd { + arg := args[0] + if n.Isddd && !call.Isddd { // Introduce ODDDARG node to represent ... allocation. - src = nod(ODDDARG, nil, nil) - arr := typArray(n2.Type.Elem(), int64(len(lls))) - src.Type = ptrto(arr) // make pointer so it will be tracked - src.Lineno = n.Lineno - e.track(src) - n.Right = src + arg = nod(ODDDARG, nil, nil) + arr := typArray(n.Type.Elem(), int64(len(args))) + arg.Type = ptrto(arr) // make pointer so it will be tracked + arg.Lineno = call.Lineno + e.track(arg) + call.Right = arg } - escassignNilWhy(e, n2, src, "arg to recursive call") - if src != lls[0] { + e.escassignNilWhy(n, arg, "arg to recursive call") + if arg != args[0] { // "..." arguments are untracked - for _, n2 := range lls { + for _, a := range args { if Debug['m'] > 3 { - fmt.Printf("%v::esccall:: ... <- %S, untracked\n", linestr(lineno), n2) + fmt.Printf("%v::esccall:: ... <- %S, untracked\n", linestr(lineno), a) } - escassignSinkNilWhy(e, src, n2, "... arg to recursive call") + e.escassignSinkNilWhy(arg, a, "... arg to recursive call") } // No more PPARAM processing, but keep // going for PPARAMOUT. - lls = nil + args = nil continue } - lls = lls[1:] + args = args[1:] case PPARAMOUT: - nE.Escretval.Append(n2) + cE.Retval.Append(n) } } @@ -1552,48 +1553,48 @@ func esccall(e *EscState, n *Node, up *Node) { } // Imported or completely analyzed function. Use the escape tags. - if nE.Escretval.Len() != 0 { - Fatalf("esc already decorated call %+v\n", n) + if cE.Retval.Len() != 0 { + Fatalf("esc already decorated call %+v\n", call) } if Debug['m'] > 3 { - fmt.Printf("%v::esccall:: %S not recursive\n", linestr(lineno), n) + fmt.Printf("%v::esccall:: %S not recursive\n", linestr(lineno), call) } // set up out list on this call node with dummy auto ONAMES in the current (calling) function. - initEscretval(e, n, fntype) + e.initEscRetval(call, fntype) - // print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval); + // print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, e.nodeEscState(call).Retval); // Receiver. - if n.Op != OCALLFUNC { - t := fntype.Recv() - src := n.Left.Left - if haspointers(t.Type) { - escassignfromtag(e, t.Note, nE.Escretval, src) + if call.Op != OCALLFUNC { + rf := fntype.Recv() + r := call.Left.Left + if haspointers(rf.Type) { + e.escassignfromtag(rf.Note, cE.Retval, r) } } - var src *Node - note := "" + var arg *Node + var note string + param, it := iterFields(fntype.Params()) i := 0 - lls := ll.Slice() - for t, it := iterFields(fntype.Params()); i < len(lls); i++ { - src = lls[i] - note = t.Note - if t.Isddd && !n.Isddd { + for ; i < len(args); i++ { + arg = args[i] + note = param.Note + if param.Isddd && !call.Isddd { // Introduce ODDDARG node to represent ... allocation. - src = nod(ODDDARG, nil, nil) - src.Lineno = n.Lineno - arr := typArray(t.Type.Elem(), int64(len(lls)-i)) - src.Type = ptrto(arr) // make pointer so it will be tracked - e.track(src) - n.Right = src + arg = nod(ODDDARG, nil, nil) + arg.Lineno = call.Lineno + arr := typArray(param.Type.Elem(), int64(len(args)-i)) + arg.Type = ptrto(arr) // make pointer so it will be tracked + e.track(arg) + call.Right = arg } - if haspointers(t.Type) { - if escassignfromtag(e, note, nE.Escretval, src)&EscMask == EscNone && up.Op != ODEFER && up.Op != OPROC { - a := src + if haspointers(param.Type) { + if e.escassignfromtag(note, cE.Retval, arg)&EscMask == EscNone && parent.Op != ODEFER && parent.Op != OPROC { + a := arg for a.Op == OCONVNOP { a = a.Left } @@ -1604,8 +1605,8 @@ func esccall(e *EscState, n *Node, up *Node) { // synthesizing this expression can be reclaimed when // the function returns. // This 'noescape' is even stronger than the usual esc == EscNone. - // src->esc == EscNone means that src does not escape the current function. - // src->noescape = 1 here means that src does not escape this statement + // arg.Esc == EscNone means that arg does not escape the current function. + // arg.Noescape = true here means that arg does not escape this statement // in the current function. case OCALLPART, OCLOSURE, @@ -1619,34 +1620,34 @@ func esccall(e *EscState, n *Node, up *Node) { } } - if src != lls[i] { - // This occurs when function parameter type Isddd and n not Isddd + if arg != args[i] { + // This occurs when function parameter field Isddd and call not Isddd break } if note == uintptrEscapesTag { - escassignSinkNilWhy(e, src, src, "escaping uintptr") + e.escassignSinkNilWhy(arg, arg, "escaping uintptr") } - t = it.Next() + param = it.Next() } // Store arguments into slice for ... arg. - for ; i < len(lls); i++ { + for ; i < len(args); i++ { if Debug['m'] > 3 { - fmt.Printf("%v::esccall:: ... <- %S\n", linestr(lineno), lls[i]) + fmt.Printf("%v::esccall:: ... <- %S\n", linestr(lineno), args[i]) } if note == uintptrEscapesTag { - escassignSinkNilWhy(e, src, lls[i], "arg to uintptrescapes ...") + e.escassignSinkNilWhy(arg, args[i], "arg to uintptrescapes ...") } else { - escassignNilWhy(e, src, lls[i], "arg to ...") + e.escassignNilWhy(arg, args[i], "arg to ...") } } } // escflows records the link src->dst in dst, throwing out some quick wins, // and also ensuring that dst is noted as a flow destination. -func escflows(e *EscState, dst, src *Node, why *EscStep) { +func (e *EscState) escflows(dst, src *Node, why *EscStep) { if dst == nil || src == nil || dst == src { return } @@ -1661,7 +1662,7 @@ func escflows(e *EscState, dst, src *Node, why *EscStep) { } dstE := e.nodeEscState(dst) - if len(dstE.Escflowsrc) == 0 { + if len(dstE.Flowsrc) == 0 { e.dsts = append(e.dsts, dst) e.dstcount++ } @@ -1669,11 +1670,11 @@ func escflows(e *EscState, dst, src *Node, why *EscStep) { e.edgecount++ if why == nil { - dstE.Escflowsrc = append(dstE.Escflowsrc, EscStep{src: src}) + dstE.Flowsrc = append(dstE.Flowsrc, EscStep{src: src}) } else { starwhy := *why starwhy.src = src // TODO: need to reconcile this w/ needs of explanations. - dstE.Escflowsrc = append(dstE.Escflowsrc, starwhy) + dstE.Flowsrc = append(dstE.Flowsrc, starwhy) } } @@ -1683,27 +1684,26 @@ func escflows(e *EscState, dst, src *Node, why *EscStep) { // so this address doesn't leak (yet). // If level == 0, it means the /value/ of this node can reach the root of this flood. // so if this node is an OADDR, its argument should be marked as escaping iff -// its currfn/e->loopdepth are different from the flood's root. +// its currfn/e.loopdepth are different from the flood's root. // Once an object has been moved to the heap, all of its upstream should be considered // escaping to the global scope. -func escflood(e *EscState, dst *Node) { +func (e *EscState) escflood(dst *Node) { switch dst.Op { case ONAME, OCLOSURE: - break - default: return } dstE := e.nodeEscState(dst) if Debug['m'] > 2 { - fmt.Printf("\nescflood:%d: dst %S scope:%v[%d]\n", e.walkgen, dst, e.curfnSym(dst), dstE.Escloopdepth) + fmt.Printf("\nescflood:%d: dst %S scope:%v[%d]\n", e.walkgen, dst, e.curfnSym(dst), dstE.Loopdepth) } - for i, l := range dstE.Escflowsrc { + for i := range dstE.Flowsrc { e.walkgen++ - dstE.Escflowsrc[i].parent = nil - escwalk(e, levelFrom(0), dst, l.src, &dstE.Escflowsrc[i]) + s := &dstE.Flowsrc[i] + s.parent = nil + e.escwalk(levelFrom(0), dst, s.src, s) } } @@ -1740,11 +1740,11 @@ func (es *EscStep) describe(src *Node) { const NOTALOOPDEPTH = -1 -func escwalk(e *EscState, level Level, dst *Node, src *Node, step *EscStep) { - escwalkBody(e, level, dst, src, step, NOTALOOPDEPTH) +func (e *EscState) escwalk(level Level, dst *Node, src *Node, step *EscStep) { + e.escwalkBody(level, dst, src, step, NOTALOOPDEPTH) } -func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, extraloopdepth int32) { +func (e *EscState) escwalkBody(level Level, dst *Node, src *Node, step *EscStep, extraloopdepth int32) { if src.Op == OLITERAL { return } @@ -1753,12 +1753,12 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, // Esclevels are vectors, do not compare as integers, // and must use "min" of old and new to guarantee // convergence. - level = level.min(srcE.Esclevel) - if level == srcE.Esclevel { + level = level.min(srcE.Level) + if level == srcE.Level { // Have we been here already with an extraloopdepth, // or is the extraloopdepth provided no improvement on // what's already been seen? - if srcE.Maxextraloopdepth >= extraloopdepth || srcE.Escloopdepth >= extraloopdepth { + if srcE.Maxextraloopdepth >= extraloopdepth || srcE.Loopdepth >= extraloopdepth { return } srcE.Maxextraloopdepth = extraloopdepth @@ -1768,8 +1768,8 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, } srcE.Walkgen = e.walkgen - srcE.Esclevel = level - modSrcLoopdepth := srcE.Escloopdepth + srcE.Level = level + modSrcLoopdepth := srcE.Loopdepth if extraloopdepth > modSrcLoopdepth { modSrcLoopdepth = extraloopdepth @@ -1777,7 +1777,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, if Debug['m'] > 2 { fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %S(%0j) scope:%v[%d] extraloopdepth=%v\n", - level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", src.Op, src, src, e.curfnSym(src), srcE.Escloopdepth, extraloopdepth) + 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) } e.pdepth++ @@ -1820,13 +1820,13 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, } } - leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Escloopdepth < modSrcLoopdepth + leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Loopdepth < modSrcLoopdepth leaks = leaks || level.int() <= 0 && dst.Esc&EscMask == EscHeap osrcesc = src.Esc switch src.Op { case ONAME: - if src.Class == PPARAM && (leaks || dstE.Escloopdepth < 0) && src.Esc&EscMask < EscScope { + if src.Class == PPARAM && (leaks || dstE.Loopdepth < 0) && src.Esc&EscMask < EscScope { if level.guaranteedDereference() > 0 { src.Esc = escMax(EscContentEscapes|src.Esc, EscNone) if Debug['m'] != 0 { @@ -1837,7 +1837,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, } } else { Warnl(src.Lineno, "leaking param content: %S level=%v dst.eld=%v src.eld=%v dst=%S", - src, level, dstE.Escloopdepth, modSrcLoopdepth, dst) + src, level, dstE.Loopdepth, modSrcLoopdepth, dst) } } } else { @@ -1849,7 +1849,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, step.describe(src) } else { Warnl(src.Lineno, "leaking param: %S level=%v dst.eld=%v src.eld=%v dst=%S", - src, level, dstE.Escloopdepth, modSrcLoopdepth, dst) + src, level, dstE.Loopdepth, modSrcLoopdepth, dst) } } } @@ -1862,7 +1862,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, Warnl(src.Lineno, "leaking closure reference %S", src) step.describe(src) } - escwalk(e, level, dst, src.Name.Defn, e.stepWalk(dst, src.Name.Defn, "closure-var", step)) + e.escwalk(level, dst, src.Name.Defn, e.stepWalk(dst, src.Name.Defn, "closure-var", step)) } case OPTRLIT, OADDR: @@ -1879,21 +1879,21 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, } if Debug['m'] > 2 { Warnl(src.Lineno, "%S escapes to heap, level=%v, dst=%v dst.eld=%v, src.eld=%v", - p, level, dst, dstE.Escloopdepth, modSrcLoopdepth) + p, level, dst, dstE.Loopdepth, modSrcLoopdepth) } else { Warnl(src.Lineno, "%S escapes to heap", p) step.describe(src) } } addrescapes(src.Left) - escwalkBody(e, level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step), modSrcLoopdepth) + e.escwalkBody(level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step), modSrcLoopdepth) extraloopdepth = modSrcLoopdepth // passes to recursive case, seems likely a no-op } else { - escwalk(e, level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step)) + e.escwalk(level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step)) } case OAPPEND: - escwalk(e, level, dst, src.List.First(), e.stepWalk(dst, src.List.First(), "append-first-arg", step)) + e.escwalk(level, dst, src.List.First(), e.stepWalk(dst, src.List.First(), "append-first-arg", step)) case ODDDARG: if leaks { @@ -1909,7 +1909,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, case OSLICELIT: for _, n1 := range src.List.Slice() { - escwalk(e, level.dec(), dst, n1.Right, e.stepWalk(dst, n1.Right, "slice-literal-element", step)) + e.escwalk(level.dec(), dst, n1.Right, e.stepWalk(dst, n1.Right, "slice-literal-element", step)) } fallthrough @@ -1939,7 +1939,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, case ODOT, ODOTTYPE: - escwalk(e, level, dst, src.Left, e.stepWalk(dst, src.Left, "dot", step)) + e.escwalk(level, dst, src.Left, e.stepWalk(dst, src.Left, "dot", step)) case OSLICE, @@ -1947,21 +1947,21 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, OSLICE3, OSLICE3ARR, OSLICESTR: - escwalk(e, level, dst, src.Left, e.stepWalk(dst, src.Left, "slice", step)) + e.escwalk(level, dst, src.Left, e.stepWalk(dst, src.Left, "slice", step)) case OINDEX: if src.Left.Type.IsArray() { - escwalk(e, level, dst, src.Left, e.stepWalk(dst, src.Left, "fixed-array-index-of", step)) + e.escwalk(level, dst, src.Left, e.stepWalk(dst, src.Left, "fixed-array-index-of", step)) break } fallthrough case ODOTPTR: - escwalk(e, level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "dot of pointer", step)) + e.escwalk(level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "dot of pointer", step)) case OINDEXMAP: - escwalk(e, level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "map index", step)) + e.escwalk(level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "map index", step)) case OIND: - escwalk(e, level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "indirection", step)) + e.escwalk(level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "indirection", step)) // In this case a link went directly to a call, but should really go // to the dummy .outN outputs that were created for the call that @@ -1969,23 +1969,25 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, // See e.g. #10466 // This can only happen with functions returning a single result. case OCALLMETH, OCALLFUNC, OCALLINTER: - if srcE.Escretval.Len() != 0 { + if srcE.Retval.Len() != 0 { if Debug['m'] > 2 { fmt.Printf("%v:[%d] dst %S escwalk replace src: %S with %S\n", linestr(lineno), e.loopdepth, - dst, src, srcE.Escretval.First()) + dst, src, srcE.Retval.First()) } - src = srcE.Escretval.First() + src = srcE.Retval.First() srcE = e.nodeEscState(src) } } recurse: level = level.copy() - for i, ll := range srcE.Escflowsrc { - srcE.Escflowsrc[i].parent = step - escwalkBody(e, level, dst, ll.src, &srcE.Escflowsrc[i], extraloopdepth) - srcE.Escflowsrc[i].parent = nil + + for i := range srcE.Flowsrc { + s := &srcE.Flowsrc[i] + s.parent = step + e.escwalkBody(level, dst, s.src, s, extraloopdepth) + s.parent = nil } e.pdepth-- @@ -2002,8 +2004,8 @@ var unsafeUintptrTag = "unsafe-uintptr" // marked go:uintptrescapes. const uintptrEscapesTag = "uintptr-escapes" -func esctag(e *EscState, func_ *Node) { - func_.Esc = EscFuncTagged +func (e *EscState) esctag(fn *Node) { + fn.Esc = EscFuncTagged name := func(s *Sym, narg int) string { if s != nil { @@ -2014,11 +2016,11 @@ func esctag(e *EscState, func_ *Node) { // External functions are assumed unsafe, // unless //go:noescape is given before the declaration. - if func_.Nbody.Len() == 0 { - if func_.Noescape { - for _, t := range func_.Type.Params().Fields().Slice() { - if haspointers(t.Type) { - t.Note = mktag(EscNone) + if fn.Nbody.Len() == 0 { + if fn.Noescape { + for _, f := range fn.Type.Params().Fields().Slice() { + if haspointers(f.Type) { + f.Note = mktag(EscNone) } } } @@ -2030,44 +2032,41 @@ func esctag(e *EscState, func_ *Node) { // but we are reusing the ability to annotate an individual function // argument and pass those annotations along to importing code. narg := 0 - for _, t := range func_.Type.Params().Fields().Slice() { + for _, f := range fn.Type.Params().Fields().Slice() { narg++ - if t.Type.Etype == TUINTPTR { + if f.Type.Etype == TUINTPTR { if Debug['m'] != 0 { - Warnl(func_.Lineno, "%v assuming %v is unsafe uintptr", funcSym(func_), name(t.Sym, narg)) + Warnl(fn.Lineno, "%v assuming %v is unsafe uintptr", funcSym(fn), name(f.Sym, narg)) } - t.Note = unsafeUintptrTag + f.Note = unsafeUintptrTag } } return } - if func_.Func.Pragma&UintptrEscapes != 0 { + if fn.Func.Pragma&UintptrEscapes != 0 { narg := 0 - for _, t := range func_.Type.Params().Fields().Slice() { + for _, f := range fn.Type.Params().Fields().Slice() { narg++ - if t.Type.Etype == TUINTPTR { + if f.Type.Etype == TUINTPTR { if Debug['m'] != 0 { - Warnl(func_.Lineno, "%v marking %v as escaping uintptr", funcSym(func_), name(t.Sym, narg)) + Warnl(fn.Lineno, "%v marking %v as escaping uintptr", funcSym(fn), name(f.Sym, narg)) } - t.Note = uintptrEscapesTag + f.Note = uintptrEscapesTag } - if t.Isddd && t.Type.Elem().Etype == TUINTPTR { + if f.Isddd && f.Type.Elem().Etype == TUINTPTR { // final argument is ...uintptr. if Debug['m'] != 0 { - Warnl(func_.Lineno, "%v marking %v as escaping ...uintptr", funcSym(func_), name(t.Sym, narg)) + Warnl(fn.Lineno, "%v marking %v as escaping ...uintptr", funcSym(fn), name(f.Sym, narg)) } - t.Note = uintptrEscapesTag + f.Note = uintptrEscapesTag } } } - savefn := Curfn - Curfn = func_ - - for _, ln := range Curfn.Func.Dcl { + for _, ln := range fn.Func.Dcl { if ln.Op != ONAME { continue } @@ -2083,9 +2082,6 @@ func esctag(e *EscState, func_ *Node) { case EscHeap, // touched by escflood, moved to heap EscScope: // touched by escflood, value leaves scope - break } } - - Curfn = savefn } diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 1fa7182705..a62865d815 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -330,8 +330,8 @@ func (n *Node) jconv(s fmt.State, flag FmtFlag) { fmt.Fprintf(s, " esc(%d)", n.Esc) } - if e, ok := n.Opt().(*NodeEscState); ok && e.Escloopdepth != 0 { - fmt.Fprintf(s, " ld(%d)", e.Escloopdepth) + if e, ok := n.Opt().(*NodeEscState); ok && e.Loopdepth != 0 { + fmt.Fprintf(s, " ld(%d)", e.Loopdepth) } if c == 0 && n.Typecheck != 0 {