aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/escape
diff options
context:
space:
mode:
authorMatthew Dempsky <mdempsky@google.com>2021-01-02 02:40:42 -0800
committerMatthew Dempsky <mdempsky@google.com>2021-01-03 06:16:32 +0000
commitb1747756e30a4e1ea0698ddbbb08f5cb7d97b1ba (patch)
tree71f57c7f8f4ef0027fc1bb6fe7d1371d0c3d4a83 /src/cmd/compile/internal/escape
parentf2538033c08a8c215a19610680d66f5909c5bcdd (diff)
downloadgo-b1747756e30a4e1ea0698ddbbb08f5cb7d97b1ba.tar.gz
go-b1747756e30a4e1ea0698ddbbb08f5cb7d97b1ba.zip
[dev.regabi] cmd/compile: reorganize escape analysis somewhat
To do closure conversion during escape analysis, we need to walk the AST in order. So this CL makes a few changes: 1. Function literals are walked where they appear in their enclosing function, rather than as independent functions. 2. Walking "range" and "switch" statements is reordered to visit the X/Tag expression up front, before the body. 3. Most assignments are refactored to use a new assignList helper, which handles 1:1, 2:1, and N:N assignments. N:1 function call assignments are still handled directly by the OAS2FUNC case. 4. A latent missed-optimization in escape.addr is fixed: the ONAMEOFFSET case was failing to update k with the result of calling e.addr(n.Name_). In partice, this probably wasn't an issue because ONAMEOFFSET is likely only used for PEXTERN variables (which are treated as heap memory anyway) or code generated by walk (which has already gone through escape analysis). 5. Finally, don't replace k with discardHole at the end of escape.addr. This is already handled at the start of escape.expr, and we'll want to be able to access the hole's location after escape.expr returns. Passes toolstash -cmp. Change-Id: I2325234346b12b10056a360c489692bab8fdbd93 Reviewed-on: https://go-review.googlesource.com/c/go/+/281003 Trust: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Diffstat (limited to 'src/cmd/compile/internal/escape')
-rw-r--r--src/cmd/compile/internal/escape/escape.go110
1 files changed, 59 insertions, 51 deletions
diff --git a/src/cmd/compile/internal/escape/escape.go b/src/cmd/compile/internal/escape/escape.go
index 17770ffbbc..1aba0a3fd2 100644
--- a/src/cmd/compile/internal/escape/escape.go
+++ b/src/cmd/compile/internal/escape/escape.go
@@ -201,10 +201,12 @@ func Batch(fns []*ir.Func, recursive bool) {
// 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()
@@ -219,8 +221,8 @@ func (b *batch) with(fn *ir.Func) *escape {
}
}
-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)
}
@@ -237,8 +239,8 @@ func (e *escape) initFunc() {
}
}
-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.
@@ -366,54 +368,52 @@ func (e *escape) stmt(n ir.Node) {
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)
@@ -425,28 +425,24 @@ func (e *escape) stmt(n ir.Node) {
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)
@@ -455,9 +451,11 @@ func (e *escape) stmt(n ir.Node) {
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:
@@ -694,6 +692,10 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {
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 {
@@ -795,7 +797,7 @@ func (e *escape) addr(n ir.Node) hole {
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)
@@ -815,10 +817,6 @@ func (e *escape) addr(n ir.Node) hole {
e.assignHeap(n.Index, "key of map put", n)
}
- if !n.Type().HasPointers() {
- k = e.discardHole()
- }
-
return k
}
@@ -830,6 +828,16 @@ func (e *escape) addrs(l ir.Nodes) []hole {
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.