From 99732b9070ea23c13f7ed9ff8518304e34942ad4 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 22 Jun 2021 20:53:14 -0700 Subject: [PATCH] [dev.typeparams] cmd/compile: refactor escape analysis of calls This CL is a prep refactoring for an upcoming CL to move go/defer wrapping into escape analysis. That CL is unfortunately unavoidably complex and subtle, so this CL takes care of some more mundane refactoring details. Change-Id: Ifbefe1d522a8d57066646be09536437f42e7082c Reviewed-on: https://go-review.googlesource.com/c/go/+/330251 Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Trust: Matthew Dempsky Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/escape/call.go | 78 ++++++++++++++----------- src/cmd/compile/internal/escape/expr.go | 2 +- src/cmd/compile/internal/escape/stmt.go | 7 +-- 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/cmd/compile/internal/escape/call.go b/src/cmd/compile/internal/escape/call.go index 28a3b679a5..8511259d47 100644 --- a/src/cmd/compile/internal/escape/call.go +++ b/src/cmd/compile/internal/escape/call.go @@ -13,26 +13,24 @@ import ( // call evaluates a call expressions, including builtin calls. ks // should contain the holes representing where the function callee's -// results flows; where is the OGO/ODEFER context of the call, if any. -func (e *escape) call(ks []hole, call, where ir.Node) { - topLevelDefer := where != nil && where.Op() == ir.ODEFER && e.loopDepth == 1 - if topLevelDefer { - // force stack allocation of defer record, unless - // open-coded defers are used (see ssa.go) - where.SetEsc(ir.EscNever) - } +// results flows. +func (e *escape) call(ks []hole, call ir.Node) { + e.callCommon(ks, call, nil) +} - argument := func(k hole, arg ir.Node) { - if topLevelDefer { - // Top level defers arguments don't escape to - // heap, but they do need to last until end of - // function. - k = e.later(k) - } else if where != nil { - k = e.heapHole() +func (e *escape) callCommon(ks []hole, call ir.Node, where *ir.GoDeferStmt) { + argument := func(k hole, argp *ir.Node) { + if where != nil { + if where.Esc() == ir.EscNever { + // Top-level defers arguments don't escape to heap, + // but they do need to last until end of function. + k = e.later(k) + } else { + k = e.heapHole() + } } - e.expr(k.note(call, "call parameter"), arg) + e.expr(k.note(call, "call parameter"), *argp) } switch call.Op() { @@ -70,15 +68,15 @@ func (e *escape) call(ks []hole, call, where ir.Node) { } if r := fntype.Recv(); r != nil { - argument(e.tagHole(ks, fn, r), call.X.(*ir.SelectorExpr).X) + argument(e.tagHole(ks, fn, r), &call.X.(*ir.SelectorExpr).X) } else { // Evaluate callee function expression. - argument(e.discardHole(), call.X) + argument(e.discardHole(), &call.X) } args := call.Args for i, param := range fntype.Params().FieldSlice() { - argument(e.tagHole(ks, fn, param), args[i]) + argument(e.tagHole(ks, fn, param), &args[i]) } case ir.OAPPEND: @@ -93,54 +91,66 @@ func (e *escape) call(ks []hole, call, where ir.Node) { if args[0].Type().Elem().HasPointers() { appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice")) } - argument(appendeeK, args[0]) + argument(appendeeK, &args[0]) if call.IsDDD { appendedK := e.discardHole() if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() { appendedK = e.heapHole().deref(call, "appended slice...") } - argument(appendedK, args[1]) + argument(appendedK, &args[1]) } else { - for _, arg := range args[1:] { - argument(e.heapHole(), arg) + for i := 1; i < len(args); i++ { + argument(e.heapHole(), &args[i]) } } case ir.OCOPY: call := call.(*ir.BinaryExpr) - argument(e.discardHole(), call.X) + argument(e.discardHole(), &call.X) copiedK := e.discardHole() if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() { copiedK = e.heapHole().deref(call, "copied slice") } - argument(copiedK, call.Y) + argument(copiedK, &call.Y) case ir.OPANIC: call := call.(*ir.UnaryExpr) - argument(e.heapHole(), call.X) + argument(e.heapHole(), &call.X) case ir.OCOMPLEX: call := call.(*ir.BinaryExpr) - argument(e.discardHole(), call.X) - argument(e.discardHole(), call.Y) + argument(e.discardHole(), &call.X) + argument(e.discardHole(), &call.Y) case ir.ODELETE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: call := call.(*ir.CallExpr) - for _, arg := range call.Args { - argument(e.discardHole(), arg) + for i := range call.Args { + argument(e.discardHole(), &call.Args[i]) } case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE: call := call.(*ir.UnaryExpr) - argument(e.discardHole(), call.X) + argument(e.discardHole(), &call.X) case ir.OUNSAFEADD, ir.OUNSAFESLICE: call := call.(*ir.BinaryExpr) - argument(ks[0], call.X) - argument(e.discardHole(), call.Y) + argument(ks[0], &call.X) + argument(e.discardHole(), &call.Y) } } +func (e *escape) goDeferStmt(n *ir.GoDeferStmt) { + topLevelDefer := n.Op() == ir.ODEFER && e.loopDepth == 1 + if topLevelDefer { + // force stack allocation of defer record, unless + // open-coded defers are used (see ssa.go) + n.SetEsc(ir.EscNever) + } + + e.stmts(n.Call.Init()) + e.callCommon(nil, n.Call, n) +} + // tagHole returns a hole for evaluating an argument passed to param. // ks should contain the holes representing where the function // callee's results flows. fn is the statically-known callee function, diff --git a/src/cmd/compile/internal/escape/expr.go b/src/cmd/compile/internal/escape/expr.go index 5b280c76f1..cb95221dd5 100644 --- a/src/cmd/compile/internal/escape/expr.go +++ b/src/cmd/compile/internal/escape/expr.go @@ -139,7 +139,7 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) { e.discard(n.X) case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE: - e.call([]hole{k}, n, nil) + e.call([]hole{k}, n) case ir.ONEW: n := n.(*ir.UnaryExpr) diff --git a/src/cmd/compile/internal/escape/stmt.go b/src/cmd/compile/internal/escape/stmt.go index d3e47290d3..0bdb07b278 100644 --- a/src/cmd/compile/internal/escape/stmt.go +++ b/src/cmd/compile/internal/escape/stmt.go @@ -163,7 +163,7 @@ func (e *escape) stmt(n ir.Node) { n := n.(*ir.AssignListStmt) e.stmts(n.Rhs[0].Init()) ks := e.addrs(n.Lhs) - e.call(ks, n.Rhs[0], nil) + e.call(ks, n.Rhs[0]) e.reassigned(ks, n) case ir.ORETURN: n := n.(*ir.ReturnStmt) @@ -174,11 +174,10 @@ func (e *escape) stmt(n ir.Node) { } 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) + e.call(nil, n) case ir.OGO, ir.ODEFER: n := n.(*ir.GoDeferStmt) - e.stmts(n.Call.Init()) - e.call(nil, n.Call, n) + e.goDeferStmt(n) case ir.OTAILCALL: // TODO(mdempsky): Treat like a normal call? esc.go used to just ignore it.