Commit 0a0f4bc1 authored by Hiroshi Ioka's avatar Hiroshi Ioka Committed by David Chase

cmd/compile/internal/gc: cleanup esc.go

* convert important functions to methods
* rename EscXXX to XXX in NodeEscState
* rename local variables more friendly
* simplify redundant code
* update comments

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