]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile/internal/escape: cleanup go/defer normalization cruft
authorMatthew Dempsky <mdempsky@google.com>
Wed, 16 Aug 2023 22:16:19 +0000 (15:16 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 17 Aug 2023 19:37:33 +0000 (19:37 +0000)
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>
src/cmd/compile/internal/escape/call.go

index eb87954299e052ace253665938bb5fe668177ec3..2ba1955b556bda5e14f8ca1a7428a430990d3faa 100644 (file)
@@ -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)
-       }
-}
-
-// 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)
+               unsafeUintptr(arg)
        }
-
-       *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
 }