]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: rewrite method calls during typecheck
authorCuong Manh Le <cuong.manhle.vn@gmail.com>
Fri, 25 Jun 2021 15:24:29 +0000 (22:24 +0700)
committerCuong Manh Le <cuong.manhle.vn@gmail.com>
Sat, 26 Jun 2021 08:39:47 +0000 (08:39 +0000)
CL 330671 move rewriting method call to method expression to escape
analysis. This CL move the rewriting up further, into typecheck. It
helps simplify the code for dowstream passes, as they now only have to
deal with OCALLFUNC.

There're two notes:

 - For -G=3, we can't rewrite d.M() where d is an instantiated receiver
   in transformCall, but let irgen.stencil to rewrite it.

 - Escape analysis still have to check for rewriting method calls, as
   the devirtualization pass can still generate OCALLMETH.

Does not pass toolstash, since when the export data now contains method
expression calls instead of method calls.

Change-Id: I77465ef04d50dc4efedddca7eb55b3fc9483db0e
Reviewed-on: https://go-review.googlesource.com/c/go/+/330831
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/cmd/compile/internal/escape/call.go
src/cmd/compile/internal/inline/inl.go
src/cmd/compile/internal/noder/reader.go
src/cmd/compile/internal/noder/transform.go
src/cmd/compile/internal/typecheck/func.go
src/cmd/compile/internal/walk/convert.go
src/cmd/compile/internal/walk/expr.go
src/cmd/compile/internal/walk/order.go
src/cmd/compile/internal/walk/stmt.go
src/cmd/compile/internal/walk/walk.go

index 62727a8ef860e3f0a72087b456ee75857988f32e..46bfe65aff1b7b2f751aca330341983e3da0d244 100644 (file)
@@ -68,7 +68,7 @@ func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir
                                fn = ir.MethodExprName(v)
                        }
                case ir.OCALLMETH:
-                       fn = ir.MethodExprName(call.X)
+                       base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
                }
 
                fntype := call.X.Type()
index 0620191bbf657b68891ae6cfb298596f60761bec..3086d093c0485d433bbb45228078577cf7de4df9 100644 (file)
@@ -273,6 +273,19 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
                                }
                        }
                }
+               if n.X.Op() == ir.OMETHEXPR {
+                       if meth := ir.MethodExprName(n.X); meth != nil {
+                               fn := meth.Func
+                               if fn != nil && types.IsRuntimePkg(fn.Sym().Pkg) && fn.Sym().Name == "heapBits.nextArena" {
+                                       // Special case: explicitly allow
+                                       // mid-stack inlining of
+                                       // runtime.heapBits.next even though
+                                       // it calls slow-path
+                                       // runtime.heapBits.nextArena.
+                                       break
+                               }
+                       }
+               }
 
                if ir.IsIntrinsicCall(n) {
                        // Treat like any other node.
@@ -287,28 +300,8 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
                // Call cost for non-leaf inlining.
                v.budget -= v.extraCallCost
 
-       // Call is okay if inlinable and we have the budget for the body.
        case ir.OCALLMETH:
-               n := n.(*ir.CallExpr)
-               t := n.X.Type()
-               if t == nil {
-                       base.Fatalf("no function type for [%p] %+v\n", n.X, n.X)
-               }
-               fn := ir.MethodExprName(n.X).Func
-               if types.IsRuntimePkg(fn.Sym().Pkg) && fn.Sym().Name == "heapBits.nextArena" {
-                       // Special case: explicitly allow
-                       // mid-stack inlining of
-                       // runtime.heapBits.next even though
-                       // it calls slow-path
-                       // runtime.heapBits.nextArena.
-                       break
-               }
-               if fn.Inl != nil {
-                       v.budget -= fn.Inl.Cost
-                       break
-               }
-               // Call cost for non-leaf inlining.
-               v.budget -= v.extraCallCost
+               base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
 
        // Things that are too hairy, irrespective of the budget
        case ir.OCALL, ir.OCALLINTER:
@@ -575,7 +568,9 @@ func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.No
        case ir.ODEFER, ir.OGO:
                n := n.(*ir.GoDeferStmt)
                switch call := n.Call; call.Op() {
-               case ir.OCALLFUNC, ir.OCALLMETH:
+               case ir.OCALLMETH:
+                       base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
+               case ir.OCALLFUNC:
                        call := call.(*ir.CallExpr)
                        call.NoInline = true
                }
@@ -585,11 +580,18 @@ func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.No
        case ir.OCLOSURE:
                return n
        case ir.OCALLMETH:
-               // Prevent inlining some reflect.Value methods when using checkptr,
-               // even when package reflect was compiled without it (#35073).
+               base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
+       case ir.OCALLFUNC:
                n := n.(*ir.CallExpr)
-               if s := ir.MethodExprName(n.X).Sym(); base.Debug.Checkptr != 0 && types.IsReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
-                       return n
+               if n.X.Op() == ir.OMETHEXPR {
+                       // Prevent inlining some reflect.Value methods when using checkptr,
+                       // even when package reflect was compiled without it (#35073).
+                       if meth := ir.MethodExprName(n.X); meth != nil {
+                               s := meth.Sym()
+                               if base.Debug.Checkptr != 0 && types.IsReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
+                                       return n
+                               }
+                       }
                }
        }
 
@@ -611,7 +613,9 @@ func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.No
        // transmogrify this node itself unless inhibited by the
        // switch at the top of this function.
        switch n.Op() {
-       case ir.OCALLFUNC, ir.OCALLMETH:
+       case ir.OCALLMETH:
+               base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
+       case ir.OCALLFUNC:
                n := n.(*ir.CallExpr)
                if n.NoInline {
                        return n
@@ -631,19 +635,8 @@ func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.No
                if fn := inlCallee(call.X); fn != nil && fn.Inl != nil {
                        n = mkinlcall(call, fn, maxCost, inlMap, edit)
                }
-
        case ir.OCALLMETH:
-               call = n.(*ir.CallExpr)
-               if base.Flag.LowerM > 3 {
-                       fmt.Printf("%v:call to meth %v\n", ir.Line(n), call.X.(*ir.SelectorExpr).Sel)
-               }
-
-               // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
-               if call.X.Type() == nil {
-                       base.Fatalf("no function type for [%p] %+v\n", call.X, call.X)
-               }
-
-               n = mkinlcall(call, ir.MethodExprName(call.X).Func, maxCost, inlMap, edit)
+               base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
        }
 
        base.Pos = lno
@@ -723,7 +716,7 @@ var SSADumpInline = func(*ir.Func) {}
 // instead.
 var NewInline = func(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr { return nil }
 
-// If n is a call node (OCALLFUNC or OCALLMETH), and fn is an ONAME node for a
+// If n is a OCALLFUNC node, and fn is an ONAME node for a
 // function with an inlinable body, return an OINLCALL node that can replace n.
 // The returned node's Ninit has the parameter assignments, the Nbody is the
 // inlined function body, and (List, Rlist) contain the (input, output)
@@ -906,11 +899,7 @@ func oldInline(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr
        as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
        as.Def = true
        if call.Op() == ir.OCALLMETH {
-               sel := call.X.(*ir.SelectorExpr)
-               if sel.X == nil {
-                       base.Fatalf("method call without receiver: %+v", call)
-               }
-               as.Rhs.Append(sel.X)
+               base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
        }
        as.Rhs.Append(call.Args...)
 
index 459e1d1703c0e256fc13ef76de8862a86c4cbe9f..e5ad3f4b8ef9c5ae6a9c835e0660a7b9fbb15560 100644 (file)
@@ -1807,8 +1807,7 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp
 
        var args ir.Nodes
        if call.Op() == ir.OCALLMETH {
-               assert(call.X.Op() == ir.ODOTMETH)
-               args.Append(call.X.(*ir.SelectorExpr).X)
+               base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
        }
        args.Append(call.Args...)
 
index 946d335f0763452b0ea58a87021dc0ef10882401..30d6e34ae4ef3541347289a28166413cd21a15d3 100644 (file)
@@ -149,6 +149,9 @@ func transformCall(n *ir.CallExpr) {
        }
 
        typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args)
+       if l.Op() == ir.ODOTMETH && len(deref(n.X.Type().Recv().Type).RParams()) == 0 {
+               typecheck.FixMethodCall(n)
+       }
        if t.NumResults() == 1 {
                n.SetType(l.Type().Results().Field(0).Type)
 
index 00770c87cf809e8995e75bdf3e113bfafc771a73..20b991be56af9a4618dda11ed1d0d8c2a9d905db 100644 (file)
@@ -516,6 +516,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node {
        }
 
        typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args, func() string { return fmt.Sprintf("argument to %v", n.X) })
+       FixMethodCall(n)
        if t.NumResults() == 0 {
                return n
        }
index 5297332f6b06ff4218bf38d58b2a44a98d11419b..e659ee59f1471a9d72d444821b256668ad1c5492 100644 (file)
@@ -462,7 +462,9 @@ func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
        // TODO(mdempsky): Make stricter. We only need to exempt
        // reflect.Value.Pointer and reflect.Value.UnsafeAddr.
        switch n.X.Op() {
-       case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
+       case ir.OCALLMETH:
+               base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
+       case ir.OCALLFUNC, ir.OCALLINTER:
                return n
        }
 
index 86c6d445012468cbcfb0b15c7eb743c4343ce739..f7675c3b7d05de37db92a95c3f10381e65fb7490 100644 (file)
@@ -167,7 +167,7 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
        case ir.OCFUNC:
                return n
 
-       case ir.OCALLINTER, ir.OCALLFUNC, ir.OCALLMETH:
+       case ir.OCALLINTER, ir.OCALLFUNC:
                n := n.(*ir.CallExpr)
                return walkCall(n, init)
 
@@ -487,9 +487,12 @@ func walkAddString(n *ir.AddStringExpr, init *ir.Nodes) ir.Node {
        return r1
 }
 
-// walkCall walks an OCALLFUNC, OCALLINTER, or OCALLMETH node.
+// walkCall walks an OCALLFUNC or OCALLINTER node.
 func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
-       if n.Op() == ir.OCALLINTER || n.Op() == ir.OCALLMETH || n.X.Op() == ir.OMETHEXPR {
+       if n.Op() == ir.OCALLMETH {
+               base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
+       }
+       if n.Op() == ir.OCALLINTER || n.X.Op() == ir.OMETHEXPR {
                // We expect both interface call reflect.Type.Method and concrete
                // call reflect.(*rtype).Method.
                usemethod(n)
@@ -550,7 +553,7 @@ func walkCall1(n *ir.CallExpr, init *ir.Nodes) {
        n.SetWalked(true)
 
        if n.Op() == ir.OCALLMETH {
-               typecheck.FixMethodCall(n)
+               base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
        }
 
        args := n.Args
index 9912feba637d277407b394495d8730d5bc249e18..16a124d2ff1a542f776f95279a401092aea7864d 100644 (file)
@@ -506,15 +506,18 @@ func (o *orderState) init(n ir.Node) {
 }
 
 // call orders the call expression n.
-// n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
+// n.Op is OCALLFUNC/OCALLINTER or a builtin like OCOPY.
 func (o *orderState) call(nn ir.Node) {
        if len(nn.Init()) > 0 {
                // Caller should have already called o.init(nn).
                base.Fatalf("%v with unexpected ninit", nn.Op())
        }
+       if nn.Op() == ir.OCALLMETH {
+               base.FatalfAt(nn.Pos(), "OCALLMETH missed by typecheck")
+       }
 
        // Builtin functions.
-       if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLMETH && nn.Op() != ir.OCALLINTER {
+       if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLINTER {
                switch n := nn.(type) {
                default:
                        base.Fatalf("unexpected call: %+v", n)
@@ -707,7 +710,7 @@ func (o *orderState) stmt(n ir.Node) {
                o.out = append(o.out, n)
 
        // Special: handle call arguments.
-       case ir.OCALLFUNC, ir.OCALLINTER, ir.OCALLMETH:
+       case ir.OCALLFUNC, ir.OCALLINTER:
                n := n.(*ir.CallExpr)
                t := o.markTemp()
                o.call(n)
@@ -1147,7 +1150,10 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
 
        case ir.OCONVNOP:
                n := n.(*ir.ConvExpr)
-               if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER || n.X.Op() == ir.OCALLMETH) {
+               if n.X.Op() == ir.OCALLMETH {
+                       base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
+               }
+               if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER) {
                        call := n.X.(*ir.CallExpr)
                        // When reordering unsafe.Pointer(f()) into a separate
                        // statement, the conversion and function call must stay
@@ -1200,9 +1206,12 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
                o.out = append(o.out, nif)
                return r
 
+       case ir.OCALLMETH:
+               base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
+               panic("unreachable")
+
        case ir.OCALLFUNC,
                ir.OCALLINTER,
-               ir.OCALLMETH,
                ir.OCAP,
                ir.OCOMPLEX,
                ir.OCOPY,
index 0c216d2e8a36a237383943ae7ad62b4ecfc27149..4581bca3dfe42da847008085e9d62f9944305171 100644 (file)
@@ -40,7 +40,6 @@ func walkStmt(n ir.Node) ir.Node {
                ir.OAS2MAPR,
                ir.OCLOSE,
                ir.OCOPY,
-               ir.OCALLMETH,
                ir.OCALLINTER,
                ir.OCALL,
                ir.OCALLFUNC,
index 6e992a91b81f43f6bb3e1751d46ecd2ba26d79ef..f687127fee3c5b39f182c99beb8e9c2d68ed2bab 100644 (file)
@@ -308,7 +308,7 @@ func mayCall(n ir.Node) bool {
                default:
                        base.FatalfAt(n.Pos(), "mayCall %+v", n)
 
-               case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER,
+               case ir.OCALLFUNC, ir.OCALLINTER,
                        ir.OUNSAFEADD, ir.OUNSAFESLICE:
                        return true