// 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.
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
//
// 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
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
}
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
}
n.Esc = EscNone // until proven otherwise
nE := e.nodeEscState(n)
- nE.Escloopdepth = e.loopdepth
+ nE.Loopdepth = e.loopdepth
e.noesc = append(e.noesc, n)
}
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.
}
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 {
// 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
escapes[i] = n.Esc
}
for _, n := range e.dsts {
- escflood(e, n)
+ e.escflood(n)
}
for {
done := true
Warnl(n.Lineno, "Reflooding %v %S", e.curfnSym(n), n)
}
escapes[i] = n.Esc
- escflood(e, n)
+ e.escflood(n)
}
}
if done {
// 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)
}
}
}
}
}
+
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
}
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:
// 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
}
}
- 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
}
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++
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
}
}
}
}
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--
// 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:
}
// 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
// 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"))
}
}
// 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")
}
}
}
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
// 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:
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:
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
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.
// first result move to the heap.
case PPARAM, PPARAMOUT:
nE := e.nodeEscState(n)
- nE.Escloopdepth = 1
+ nE.Loopdepth = 1
}
}
}
// 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
}
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)
OPTRLIT,
ODDDARG,
OCALLPART:
- break
case ONAME:
if dst.Class == PEXTERN {
}
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
}
// 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
}
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.
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
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--
func mktag(mask int) string {
switch mask & EscMask {
case EscNone, EscReturn:
- break
-
default:
Fatalf("escape mktag")
}
// 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
}
if em == EscUnknown {
- escassignSinkNilWhy(e, src, src, "passed to function[unknown]")
+ e.escassignSinkNilWhy(src, src, "passed to function[unknown]")
return em
}
// 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
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++
}
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.
// 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) {
return (e &^ (bitsMaskForTag << shift)) | encodedFlow
}
-func initEscretval(e *EscState, n *Node, fntype *Type) {
- i := 0
- nE := e.nodeEscState(n)
- nE.Escretval.Set(nil) // Suspect this is not nil for indirect calls.
- for _, t := range fntype.Results().Fields().Slice() {
- src := nod(ONAME, nil, nil)
+func (e *EscState) initEscRetval(call *Node, fntype *Type) {
+ cE := e.nodeEscState(call)
+ cE.Retval.Set(nil) // Suspect this is not nil for indirect calls.
+ for i, f := range fntype.Results().Fields().Slice() {
+ ret := nod(ONAME, nil, nil)
buf := fmt.Sprintf(".out%d", i)
- i++
- src.Sym = lookup(buf)
- src.Type = t.Type
- src.Class = PAUTO
- src.Name.Curfn = Curfn
- e.nodeEscState(src).Escloopdepth = e.loopdepth
- src.Used = true
- src.Lineno = n.Lineno
- nE.Escretval.Append(src)
+ ret.Sym = lookup(buf)
+ ret.Type = f.Type
+ ret.Class = PAUTO
+ ret.Name.Curfn = Curfn
+ e.nodeEscState(ret).Loopdepth = e.loopdepth
+ ret.Used = true
+ ret.Lineno = call.Lineno
+ cE.Retval.Append(ret)
}
}
// This is a bit messier than fortunate, pulled out of esc's big
-// switch for clarity. We either have the paramnodes, which may be
+// switch for clarity. We either have the paramnodes, which may be
// connected to other things through flows or we have the parameter type
// nodes, which may be marked "noescape". Navigating the ast is slightly
// different for methods vs plain functions and for imported vs
// this-package
-func esccall(e *EscState, n *Node, up *Node) {
+func (e *EscState) esccall(call *Node, parent *Node) {
var fntype *Type
var indirect bool
var fn *Node
- switch n.Op {
+ switch call.Op {
default:
Fatalf("esccall")
case OCALLFUNC:
- fn = n.Left
+ fn = call.Left
fntype = fn.Type
indirect = fn.Op != ONAME || fn.Class != PFUNC
case OCALLMETH:
- fn = n.Left.Sym.Def
+ fn = call.Left.Sym.Def
if fn != nil {
fntype = fn.Type
} else {
- fntype = n.Left.Type
+ fntype = call.Left.Type
}
case OCALLINTER:
- fntype = n.Left.Type
+ fntype = call.Left.Type
indirect = true
}
- ll := n.List
- if n.List.Len() == 1 {
- a := n.List.First()
- if a.Type.IsFuncArgStruct() { // f(g())
- ll = e.nodeEscState(a).Escretval
+ argList := call.List
+ if argList.Len() == 1 {
+ arg := argList.First()
+ if arg.Type.IsFuncArgStruct() { // f(g())
+ argList = e.nodeEscState(arg).Retval
}
}
+ args := argList.Slice()
+
if indirect {
// We know nothing!
// Leak all the parameters
- for _, n1 := range ll.Slice() {
- escassignSinkNilWhy(e, n, n1, "parameter to indirect call")
+ for _, arg := range args {
+ e.escassignSinkNilWhy(call, arg, "parameter to indirect call")
if Debug['m'] > 3 {
- fmt.Printf("%v::esccall:: indirect call <- %S, untracked\n", linestr(lineno), n1)
+ fmt.Printf("%v::esccall:: indirect call <- %S, untracked\n", linestr(lineno), arg)
}
}
// Set up bogus outputs
- initEscretval(e, n, fntype)
+ e.initEscRetval(call, fntype)
// If there is a receiver, it also leaks to heap.
- if n.Op != OCALLFUNC {
- t := fntype.Recv()
- src := n.Left.Left
- if haspointers(t.Type) {
- escassignSinkNilWhy(e, n, src, "receiver in indirect call")
+ if call.Op != OCALLFUNC {
+ rf := fntype.Recv()
+ r := call.Left.Left
+ if haspointers(rf.Type) {
+ e.escassignSinkNilWhy(call, r, "receiver in indirect call")
}
} else { // indirect and OCALLFUNC = could be captured variables, too. (#14409)
- ll := e.nodeEscState(n).Escretval.Slice()
- for _, llN := range ll {
- escassignDereference(e, llN, fn, e.stepAssign(nil, llN, fn, "captured by called closure"))
+ rets := e.nodeEscState(call).Retval.Slice()
+ for _, ret := range rets {
+ e.escassignDereference(ret, fn, e.stepAssign(nil, ret, fn, "captured by called closure"))
}
}
return
}
- nE := e.nodeEscState(n)
+ cE := e.nodeEscState(call)
if fn != nil && fn.Op == ONAME && fn.Class == PFUNC &&
fn.Name.Defn != nil && fn.Name.Defn.Nbody.Len() != 0 && fn.Name.Param.Ntype != nil && fn.Name.Defn.Esc < EscFuncTagged {
if Debug['m'] > 3 {
- fmt.Printf("%v::esccall:: %S in recursive group\n", linestr(lineno), n)
+ fmt.Printf("%v::esccall:: %S in recursive group\n", linestr(lineno), call)
}
// function in same mutually recursive group. Incorporate into flow graph.
- // print("esc local fn: %N\n", fn->ntype);
- if fn.Name.Defn.Esc == EscFuncUnknown || nE.Escretval.Len() != 0 {
+ // print("esc local fn: %N\n", fn.Func.Ntype);
+ if fn.Name.Defn.Esc == EscFuncUnknown || cE.Retval.Len() != 0 {
Fatalf("graph inconsistency")
}
- lls := ll.Slice()
sawRcvr := false
- var src *Node
- for _, n2 := range fn.Name.Defn.Func.Dcl {
- switch n2.Class {
+ for _, n := range fn.Name.Defn.Func.Dcl {
+ switch n.Class {
case PPARAM:
- if n.Op != OCALLFUNC && !sawRcvr {
- escassignNilWhy(e, n2, n.Left.Left, "call receiver")
+ if call.Op != OCALLFUNC && !sawRcvr {
+ e.escassignNilWhy(n, call.Left.Left, "call receiver")
sawRcvr = true
continue
}
- if len(lls) == 0 {
+ if len(args) == 0 {
continue
}
- src = lls[0]
- if n2.Isddd && !n.Isddd {
+ arg := args[0]
+ if n.Isddd && !call.Isddd {
// Introduce ODDDARG node to represent ... allocation.
- src = nod(ODDDARG, nil, nil)
- arr := typArray(n2.Type.Elem(), int64(len(lls)))
- src.Type = ptrto(arr) // make pointer so it will be tracked
- src.Lineno = n.Lineno
- e.track(src)
- n.Right = src
+ arg = nod(ODDDARG, nil, nil)
+ arr := typArray(n.Type.Elem(), int64(len(args)))
+ arg.Type = ptrto(arr) // make pointer so it will be tracked
+ arg.Lineno = call.Lineno
+ e.track(arg)
+ call.Right = arg
}
- escassignNilWhy(e, n2, src, "arg to recursive call")
- if src != lls[0] {
+ e.escassignNilWhy(n, arg, "arg to recursive call")
+ if arg != args[0] {
// "..." arguments are untracked
- for _, n2 := range lls {
+ for _, a := range args {
if Debug['m'] > 3 {
- fmt.Printf("%v::esccall:: ... <- %S, untracked\n", linestr(lineno), n2)
+ fmt.Printf("%v::esccall:: ... <- %S, untracked\n", linestr(lineno), a)
}
- escassignSinkNilWhy(e, src, n2, "... arg to recursive call")
+ e.escassignSinkNilWhy(arg, a, "... arg to recursive call")
}
// No more PPARAM processing, but keep
// going for PPARAMOUT.
- lls = nil
+ args = nil
continue
}
- lls = lls[1:]
+ args = args[1:]
case PPARAMOUT:
- nE.Escretval.Append(n2)
+ cE.Retval.Append(n)
}
}
}
// 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
}
// 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,
}
}
- 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
}
}
dstE := e.nodeEscState(dst)
- if len(dstE.Escflowsrc) == 0 {
+ if len(dstE.Flowsrc) == 0 {
e.dsts = append(e.dsts, dst)
e.dstcount++
}
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)
}
}
// 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)
}
}
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
}
// 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
}
srcE.Walkgen = e.walkgen
- srcE.Esclevel = level
- modSrcLoopdepth := srcE.Escloopdepth
+ srcE.Level = level
+ modSrcLoopdepth := srcE.Loopdepth
if extraloopdepth > modSrcLoopdepth {
modSrcLoopdepth = extraloopdepth
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++
}
}
- 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 {
}
} 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 {
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)
}
}
}
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:
}
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 {
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
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,
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
// 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--
// 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 {
// 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)
}
}
}
// 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
}
case EscHeap, // touched by escflood, moved to heap
EscScope: // touched by escflood, value leaves scope
- break
}
}
-
- Curfn = savefn
}