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 (
// 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,22 +1407,20 @@ 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)
}
}
......@@ -1429,122 +1430,122 @@ func initEscretval(e *EscState, n *Node, fntype *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
}
escassignNilWhy(e, n2, src, "arg to recursive call")
if src != lls[0] {
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
}
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
}
......@@ -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 {
......
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