[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 <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
Matthew Dempsky 2021-06-22 20:53:14 -07:00
parent 1a445dab66
commit 99732b9070
3 changed files with 48 additions and 39 deletions

View file

@ -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,