mirror of https://github.com/golang/go.git
cmd/compile/internal/escape: cleanup go/defer normalization cruft
This CL removes the extra complexity from escape analysis that was only needed to support go/defer normalization. It does not affect analysis results at all. Change-Id: I75785e0cb4c4ce19bea3b8df0bf95821bd885291 Reviewed-on: https://go-review.googlesource.com/c/go/+/520261 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Auto-Submit: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
parent
2c51ea11b0
commit
b805e18fbf
|
|
@ -16,38 +16,9 @@ import (
|
|||
// should contain the holes representing where the function callee's
|
||||
// results flows.
|
||||
func (e *escape) call(ks []hole, call ir.Node) {
|
||||
var init ir.Nodes
|
||||
e.callCommon(ks, call, &init, nil)
|
||||
if len(init) != 0 {
|
||||
call.(ir.InitNode).PtrInit().Append(init...)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir.Func) {
|
||||
|
||||
// argumentPragma handles escape analysis of argument *argp to the
|
||||
// given hole. If the function callee is known, pragma is the
|
||||
// function's pragma flags; otherwise 0.
|
||||
argumentFunc := func(fn *ir.Name, k hole, argp *ir.Node) {
|
||||
e.rewriteArgument(argp, init, call, fn, wrapper)
|
||||
|
||||
e.expr(k.note(call, "call parameter"), *argp)
|
||||
}
|
||||
|
||||
argument := func(k hole, argp *ir.Node) {
|
||||
argumentFunc(nil, k, argp)
|
||||
}
|
||||
|
||||
argumentRType := func(rtypep *ir.Node) {
|
||||
rtype := *rtypep
|
||||
if rtype == nil {
|
||||
return
|
||||
}
|
||||
// common case: static rtype/itab argument, which can be evaluated within the wrapper instead.
|
||||
if addr, ok := rtype.(*ir.AddrExpr); ok && addr.Op() == ir.OADDR && addr.X.Op() == ir.OLINKSYMOFFSET {
|
||||
return
|
||||
}
|
||||
e.wrapExpr(rtype.Pos(), rtypep, init, call, wrapper)
|
||||
argument := func(k hole, arg ir.Node) {
|
||||
// TODO(mdempsky): Should be "call argument".
|
||||
e.expr(k.note(call, "call parameter"), arg)
|
||||
}
|
||||
|
||||
switch call.Op() {
|
||||
|
|
@ -83,13 +54,9 @@ func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir
|
|||
}
|
||||
}
|
||||
|
||||
var recvp *ir.Node
|
||||
var recvArg ir.Node
|
||||
if call.Op() == ir.OCALLFUNC {
|
||||
// Evaluate callee function expression.
|
||||
//
|
||||
// Note: We use argument and not argumentFunc, because while
|
||||
// call.X here may be an argument to runtime.{new,defer}proc,
|
||||
// it's not an argument to fn itself.
|
||||
calleeK := e.discardHole()
|
||||
if fn == nil { // unknown callee
|
||||
for _, k := range ks {
|
||||
|
|
@ -98,30 +65,36 @@ func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir
|
|||
// know the callee function. If a closure flows here, we
|
||||
// need to conservatively assume its results might flow to
|
||||
// the heap.
|
||||
calleeK = e.calleeHole()
|
||||
calleeK = e.calleeHole().note(call, "callee operand")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
argument(calleeK, &call.X)
|
||||
e.expr(calleeK, call.X)
|
||||
} else {
|
||||
recvp = &call.X.(*ir.SelectorExpr).X
|
||||
recvArg = call.X.(*ir.SelectorExpr).X
|
||||
}
|
||||
|
||||
// argumentParam handles escape analysis of assigning a call
|
||||
// argument to its corresponding parameter.
|
||||
argumentParam := func(param *types.Field, arg ir.Node) {
|
||||
e.rewriteArgument(arg, call, fn)
|
||||
argument(e.tagHole(ks, fn, param), arg)
|
||||
}
|
||||
|
||||
args := call.Args
|
||||
if recv := fntype.Recv(); recv != nil {
|
||||
if recvp == nil {
|
||||
if recvParam := fntype.Recv(); recvParam != nil {
|
||||
if recvArg == nil {
|
||||
// Function call using method expression. Receiver argument is
|
||||
// at the front of the regular arguments list.
|
||||
recvp = &args[0]
|
||||
args = args[1:]
|
||||
recvArg, args = args[0], args[1:]
|
||||
}
|
||||
|
||||
argumentFunc(fn, e.tagHole(ks, fn, recv), recvp)
|
||||
argumentParam(recvParam, recvArg)
|
||||
}
|
||||
|
||||
for i, param := range fntype.Params().FieldSlice() {
|
||||
argumentFunc(fn, e.tagHole(ks, fn, param), &args[i])
|
||||
argumentParam(param, args[i])
|
||||
}
|
||||
|
||||
case ir.OINLCALL:
|
||||
|
|
@ -147,65 +120,65 @@ func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir
|
|||
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 i := 1; i < len(args); i++ {
|
||||
argument(e.heapHole(), &args[i])
|
||||
argument(e.heapHole(), args[i])
|
||||
}
|
||||
}
|
||||
argumentRType(&call.RType)
|
||||
e.discard(call.RType)
|
||||
|
||||
case ir.OCOPY:
|
||||
call := call.(*ir.BinaryExpr)
|
||||
argument(e.mutatorHole(), &call.X)
|
||||
argument(e.mutatorHole(), 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)
|
||||
argumentRType(&call.RType)
|
||||
argument(copiedK, call.Y)
|
||||
e.discard(call.RType)
|
||||
|
||||
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)
|
||||
e.discard(call.X)
|
||||
e.discard(call.Y)
|
||||
|
||||
case ir.ODELETE, ir.OMAX, ir.OMIN, ir.OPRINT, ir.OPRINTN, ir.ORECOVERFP:
|
||||
call := call.(*ir.CallExpr)
|
||||
for i := range call.Args {
|
||||
argument(e.discardHole(), &call.Args[i])
|
||||
e.discard(call.Args[i])
|
||||
}
|
||||
argumentRType(&call.RType)
|
||||
e.discard(call.RType)
|
||||
|
||||
case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE:
|
||||
call := call.(*ir.UnaryExpr)
|
||||
argument(e.discardHole(), &call.X)
|
||||
e.discard(call.X)
|
||||
|
||||
case ir.OCLEAR:
|
||||
call := call.(*ir.UnaryExpr)
|
||||
argument(e.mutatorHole(), &call.X)
|
||||
argument(e.mutatorHole(), call.X)
|
||||
|
||||
case ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
|
||||
call := call.(*ir.UnaryExpr)
|
||||
argument(ks[0], &call.X)
|
||||
argument(ks[0], call.X)
|
||||
|
||||
case ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
|
||||
call := call.(*ir.BinaryExpr)
|
||||
argument(ks[0], &call.X)
|
||||
argument(e.discardHole(), &call.Y)
|
||||
argumentRType(&call.RType)
|
||||
argument(ks[0], call.X)
|
||||
e.discard(call.Y)
|
||||
e.discard(call.RType)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -244,34 +217,31 @@ func (e *escape) goDeferStmt(n *ir.GoDeferStmt) {
|
|||
e.expr(k, call.X)
|
||||
}
|
||||
|
||||
// rewriteArgument rewrites the argument *argp of the given call expression.
|
||||
// rewriteArgument rewrites the argument arg of the given call expression.
|
||||
// fn is the static callee function, if known.
|
||||
// wrapper is the go/defer wrapper function for call, if any.
|
||||
func (e *escape) rewriteArgument(argp *ir.Node, init *ir.Nodes, call ir.Node, fn *ir.Name, wrapper *ir.Func) {
|
||||
var pragma ir.PragmaFlag
|
||||
if fn != nil && fn.Func != nil {
|
||||
pragma = fn.Func.Pragma
|
||||
func (e *escape) rewriteArgument(arg ir.Node, call *ir.CallExpr, fn *ir.Name) {
|
||||
if fn == nil || fn.Func == nil {
|
||||
return
|
||||
}
|
||||
pragma := fn.Func.Pragma
|
||||
if pragma&(ir.UintptrKeepAlive|ir.UintptrEscapes) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// unsafeUintptr rewrites "uintptr(ptr)" arguments to syscall-like
|
||||
// functions, so that ptr is kept alive and/or escaped as
|
||||
// appropriate. unsafeUintptr also reports whether it modified arg0.
|
||||
unsafeUintptr := func(arg0 ir.Node) bool {
|
||||
if pragma&(ir.UintptrKeepAlive|ir.UintptrEscapes) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
unsafeUintptr := func(arg ir.Node) {
|
||||
// If the argument is really a pointer being converted to uintptr,
|
||||
// arrange for the pointer to be kept alive until the call returns,
|
||||
// by copying it into a temp and marking that temp
|
||||
// still alive when we pop the temp stack.
|
||||
if arg0.Op() != ir.OCONVNOP || !arg0.Type().IsUintptr() {
|
||||
return false
|
||||
// arrange for the pointer to be kept alive until the call
|
||||
// returns, by copying it into a temp and marking that temp still
|
||||
// alive when we pop the temp stack.
|
||||
conv, ok := arg.(*ir.ConvExpr)
|
||||
if !ok || conv.Op() != ir.OCONVNOP {
|
||||
return // not a conversion
|
||||
}
|
||||
arg := arg0.(*ir.ConvExpr)
|
||||
|
||||
if !arg.X.Type().IsUnsafePtr() {
|
||||
return false
|
||||
if !conv.X.Type().IsUnsafePtr() || !conv.Type().IsUintptr() {
|
||||
return // not an unsafe.Pointer->uintptr conversion
|
||||
}
|
||||
|
||||
// Create and declare a new pointer-typed temp variable.
|
||||
|
|
@ -279,64 +249,21 @@ func (e *escape) rewriteArgument(argp *ir.Node, init *ir.Nodes, call ir.Node, fn
|
|||
// TODO(mdempsky): This potentially violates the Go spec's order
|
||||
// of evaluations, by evaluating arg.X before any other
|
||||
// operands.
|
||||
tmp := e.wrapExpr(arg.Pos(), &arg.X, init, call, wrapper)
|
||||
tmp := e.copyExpr(conv.Pos(), conv.X, call.PtrInit())
|
||||
conv.X = tmp
|
||||
|
||||
k := e.mutatorHole()
|
||||
if pragma&ir.UintptrEscapes != 0 {
|
||||
k = e.heapHole().note(arg, "//go:uintptrescapes")
|
||||
k = e.heapHole().note(conv, "//go:uintptrescapes")
|
||||
}
|
||||
e.flow(k, e.oldLoc(tmp))
|
||||
|
||||
if pragma&ir.UintptrKeepAlive != 0 {
|
||||
call := call.(*ir.CallExpr)
|
||||
|
||||
// SSA implements CallExpr.KeepAlive using OpVarLive, which
|
||||
// doesn't support PAUTOHEAP variables. I tried changing it to
|
||||
// use OpKeepAlive, but that ran into issues of its own.
|
||||
// For now, the easy solution is to explicitly copy to (yet
|
||||
// another) new temporary variable.
|
||||
keep := tmp
|
||||
if keep.Class == ir.PAUTOHEAP {
|
||||
keep = e.copyExpr(arg.Pos(), tmp, call.PtrInit(), wrapper, false)
|
||||
}
|
||||
|
||||
keep.SetAddrtaken(true) // ensure SSA keeps the tmp variable
|
||||
call.KeepAlive = append(call.KeepAlive, keep)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
visit := func(pos src.XPos, argp *ir.Node) {
|
||||
// Optimize a few common constant expressions. By leaving these
|
||||
// untouched in the call expression, we let the wrapper handle
|
||||
// evaluating them, rather than taking up closure context space.
|
||||
switch arg := *argp; arg.Op() {
|
||||
case ir.OLITERAL, ir.ONIL, ir.OMETHEXPR:
|
||||
return
|
||||
case ir.ONAME:
|
||||
if arg.(*ir.Name).Class == ir.PFUNC {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if unsafeUintptr(*argp) {
|
||||
return
|
||||
}
|
||||
|
||||
if wrapper != nil {
|
||||
e.wrapExpr(pos, argp, init, call, wrapper)
|
||||
tmp.SetAddrtaken(true) // ensure SSA keeps the tmp variable
|
||||
call.KeepAlive = append(call.KeepAlive, tmp)
|
||||
}
|
||||
}
|
||||
|
||||
// Peel away any slice literals for better escape analyze
|
||||
// them. For example:
|
||||
//
|
||||
// go F([]int{a, b})
|
||||
//
|
||||
// If F doesn't escape its arguments, then the slice can
|
||||
// be allocated on the new goroutine's stack.
|
||||
//
|
||||
// For variadic functions, the compiler has already rewritten:
|
||||
//
|
||||
// f(a, b, c)
|
||||
|
|
@ -346,54 +273,29 @@ func (e *escape) rewriteArgument(argp *ir.Node, init *ir.Nodes, call ir.Node, fn
|
|||
// f([]T{a, b, c}...)
|
||||
//
|
||||
// So we need to look into slice elements to handle uintptr(ptr)
|
||||
// arguments to syscall-like functions correctly.
|
||||
if arg := *argp; arg.Op() == ir.OSLICELIT {
|
||||
// arguments to variadic syscall-like functions correctly.
|
||||
if arg.Op() == ir.OSLICELIT {
|
||||
list := arg.(*ir.CompLitExpr).List
|
||||
for i := range list {
|
||||
el := &list[i]
|
||||
if list[i].Op() == ir.OKEY {
|
||||
el = &list[i].(*ir.KeyExpr).Value
|
||||
for _, el := range list {
|
||||
if el.Op() == ir.OKEY {
|
||||
el = el.(*ir.KeyExpr).Value
|
||||
}
|
||||
visit(arg.Pos(), el)
|
||||
unsafeUintptr(el)
|
||||
}
|
||||
} else {
|
||||
visit(call.Pos(), argp)
|
||||
unsafeUintptr(arg)
|
||||
}
|
||||
}
|
||||
|
||||
// wrapExpr replaces *exprp with a temporary variable copy. If wrapper
|
||||
// is non-nil, the variable will be captured for use within that
|
||||
// function.
|
||||
func (e *escape) wrapExpr(pos src.XPos, exprp *ir.Node, init *ir.Nodes, call ir.Node, wrapper *ir.Func) *ir.Name {
|
||||
tmp := e.copyExpr(pos, *exprp, init, e.curfn, true)
|
||||
|
||||
if wrapper != nil {
|
||||
// Currently for "defer i.M()" if i is nil it panics at the point
|
||||
// of defer statement, not when deferred function is called. We
|
||||
// need to do the nil check outside of the wrapper.
|
||||
if call.Op() == ir.OCALLINTER && exprp == &call.(*ir.CallExpr).X.(*ir.SelectorExpr).X {
|
||||
check := ir.NewUnaryExpr(pos, ir.OCHECKNIL, ir.NewUnaryExpr(pos, ir.OITAB, tmp))
|
||||
init.Append(typecheck.Stmt(check))
|
||||
}
|
||||
|
||||
e.oldLoc(tmp).captured = true
|
||||
|
||||
tmp = ir.NewClosureVar(pos, wrapper, tmp)
|
||||
}
|
||||
|
||||
*exprp = tmp
|
||||
return tmp
|
||||
}
|
||||
|
||||
// copyExpr creates and returns a new temporary variable within fn;
|
||||
// appends statements to init to declare and initialize it to expr;
|
||||
// and escape analyzes the data flow if analyze is true.
|
||||
func (e *escape) copyExpr(pos src.XPos, expr ir.Node, init *ir.Nodes, fn *ir.Func, analyze bool) *ir.Name {
|
||||
// and escape analyzes the data flow.
|
||||
func (e *escape) copyExpr(pos src.XPos, expr ir.Node, init *ir.Nodes) *ir.Name {
|
||||
if ir.HasUniquePos(expr) {
|
||||
pos = expr.Pos()
|
||||
}
|
||||
|
||||
tmp := typecheck.TempAt(pos, fn, expr.Type())
|
||||
tmp := typecheck.TempAt(pos, e.curfn, expr.Type())
|
||||
|
||||
stmts := []ir.Node{
|
||||
ir.NewDecl(pos, ir.ODCL, tmp),
|
||||
|
|
@ -402,10 +304,8 @@ func (e *escape) copyExpr(pos src.XPos, expr ir.Node, init *ir.Nodes, fn *ir.Fun
|
|||
typecheck.Stmts(stmts)
|
||||
init.Append(stmts...)
|
||||
|
||||
if analyze {
|
||||
e.newLoc(tmp, true)
|
||||
e.stmts(stmts)
|
||||
}
|
||||
e.newLoc(tmp, true)
|
||||
e.stmts(stmts)
|
||||
|
||||
return tmp
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue