// Construct data-flow graph from syntax trees.
for _, fn := range fns {
- b.with(fn).initFunc()
+ b.initFunc(fn)
}
for _, fn := range fns {
- b.with(fn).walkFunc()
+ if !fn.IsHiddenClosure() {
+ b.walkFunc(fn)
+ }
}
b.walkAll()
}
}
-func (e *escape) initFunc() {
- fn := e.curfn
+func (b *batch) initFunc(fn *ir.Func) {
+ e := b.with(fn)
if fn.Esc() != escFuncUnknown {
base.Fatalf("unexpected node: %v", fn)
}
}
}
-func (e *escape) walkFunc() {
- fn := e.curfn
+func (b *batch) walkFunc(fn *ir.Func) {
+ e := b.with(fn)
fn.SetEsc(escFuncStarted)
// Identify labels that mark the head of an unstructured loop.
case ir.ORANGE:
// for Key, Value = range X { Body }
n := n.(*ir.RangeStmt)
- e.loopDepth++
- e.addr(n.Key)
- k := e.addr(n.Value)
- e.block(n.Body)
- e.loopDepth--
// X is evaluated outside the loop.
+ tmp := e.newLoc(nil, false)
+ e.expr(tmp.asHole(), n.X)
+
+ e.loopDepth++
+ ks := e.addrs([]ir.Node{n.Key, n.Value})
if n.X.Type().IsArray() {
- k = k.note(n, "range")
+ e.flow(ks[1].note(n, "range"), tmp)
} else {
- k = k.deref(n, "range-deref")
+ e.flow(ks[1].deref(n, "range-deref"), tmp)
}
- e.expr(e.later(k), n.X)
+
+ e.block(n.Body)
+ e.loopDepth--
case ir.OSWITCH:
n := n.(*ir.SwitchStmt)
- typesw := n.Tag != nil && n.Tag.Op() == ir.OTYPESW
- var ks []hole
- for _, cas := range n.Cases { // cases
- if typesw && n.Tag.(*ir.TypeSwitchGuard).Tag != nil {
- cv := cas.Var
- k := e.dcl(cv) // type switch variables have no ODCL.
- if cv.Type().HasPointers() {
- ks = append(ks, k.dotType(cv.Type(), cas, "switch case"))
+ if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok {
+ var ks []hole
+ if guard.Tag != nil {
+ for _, cas := range n.Cases {
+ cv := cas.Var
+ k := e.dcl(cv) // type switch variables have no ODCL.
+ if cv.Type().HasPointers() {
+ ks = append(ks, k.dotType(cv.Type(), cas, "switch case"))
+ }
}
}
-
- e.discards(cas.List)
- e.block(cas.Body)
- }
-
- if typesw {
e.expr(e.teeHole(ks...), n.Tag.(*ir.TypeSwitchGuard).X)
} else {
e.discard(n.Tag)
}
+ for _, cas := range n.Cases {
+ e.discards(cas.List)
+ e.block(cas.Body)
+ }
+
case ir.OSELECT:
n := n.(*ir.SelectStmt)
for _, cas := range n.Cases {
e.stmt(cas.Comm)
e.block(cas.Body)
}
- case ir.OSELRECV2:
- n := n.(*ir.AssignListStmt)
- e.assign(n.Lhs[0], n.Rhs[0], "selrecv", n)
- e.assign(n.Lhs[1], nil, "selrecv", n)
case ir.ORECV:
// TODO(mdempsky): Consider e.discard(n.Left).
n := n.(*ir.UnaryExpr)
case ir.OAS:
n := n.(*ir.AssignStmt)
- e.assign(n.X, n.Y, "assign", n)
+ e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
case ir.OASOP:
n := n.(*ir.AssignOpStmt)
- e.assign(n.X, n.Y, "assign", n)
+ // TODO(mdempsky): Worry about OLSH/ORSH?
+ e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
case ir.OAS2:
n := n.(*ir.AssignListStmt)
- for i, nl := range n.Lhs {
- e.assign(nl, n.Rhs[i], "assign-pair", n)
- }
+ e.assignList(n.Lhs, n.Rhs, "assign-pair", n)
case ir.OAS2DOTTYPE: // v, ok = x.(type)
n := n.(*ir.AssignListStmt)
- e.assign(n.Lhs[0], n.Rhs[0], "assign-pair-dot-type", n)
- e.assign(n.Lhs[1], nil, "assign-pair-dot-type", n)
+ e.assignList(n.Lhs, n.Rhs, "assign-pair-dot-type", n)
case ir.OAS2MAPR: // v, ok = m[k]
n := n.(*ir.AssignListStmt)
- e.assign(n.Lhs[0], n.Rhs[0], "assign-pair-mapr", n)
- e.assign(n.Lhs[1], nil, "assign-pair-mapr", n)
- case ir.OAS2RECV: // v, ok = <-ch
+ e.assignList(n.Lhs, n.Rhs, "assign-pair-mapr", n)
+ case ir.OAS2RECV, ir.OSELRECV2: // v, ok = <-ch
n := n.(*ir.AssignListStmt)
- e.assign(n.Lhs[0], n.Rhs[0], "assign-pair-receive", n)
- e.assign(n.Lhs[1], nil, "assign-pair-receive", n)
+ e.assignList(n.Lhs, n.Rhs, "assign-pair-receive", n)
case ir.OAS2FUNC:
n := n.(*ir.AssignListStmt)
case ir.ORETURN:
n := n.(*ir.ReturnStmt)
results := e.curfn.Type().Results().FieldSlice()
- for i, v := range n.Results {
- e.assign(ir.AsNode(results[i].Nname), v, "return", n)
+ dsts := make([]ir.Node, len(results))
+ for i, res := range results {
+ dsts[i] = res.Nname.(*ir.Name)
}
+ e.assignList(dsts, n.Results, "return", n)
case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
e.call(nil, n, nil)
case ir.OGO, ir.ODEFER:
case ir.OCLOSURE:
n := n.(*ir.ClosureExpr)
+ if fn := n.Func; fn.IsHiddenClosure() {
+ e.walkFunc(fn)
+ }
+
// Link addresses of captured variables to closure.
k = e.spill(k, n)
for _, v := range n.Func.ClosureVars {
k = e.oldLoc(n).asHole()
case ir.ONAMEOFFSET:
n := n.(*ir.NameOffsetExpr)
- e.addr(n.Name_)
+ k = e.addr(n.Name_)
case ir.ODOT:
n := n.(*ir.SelectorExpr)
k = e.addr(n.X)
e.assignHeap(n.Index, "key of map put", n)
}
- if !n.Type().HasPointers() {
- k = e.discardHole()
- }
-
return k
}
return ks
}
+func (e *escape) assignList(dsts, srcs []ir.Node, why string, where ir.Node) {
+ for i, dst := range dsts {
+ var src ir.Node
+ if i < len(srcs) {
+ src = srcs[i]
+ }
+ e.assign(dst, src, why, where)
+ }
+}
+
// assign evaluates the assignment dst = src.
func (e *escape) assign(dst, src ir.Node, why string, where ir.Node) {
// Filter out some no-op assignments for escape analysis.