]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: early devirtualization of interface method calls
authorMatthew Dempsky <mdempsky@google.com>
Thu, 29 Oct 2020 01:49:10 +0000 (18:49 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Thu, 29 Oct 2020 19:06:32 +0000 (19:06 +0000)
After inlining, add a pass that looks for interface calls where we can
statically determine the interface value's concrete type. If such a
case is found, insert an explicit type assertion to the concrete type
so that escape analysis can see it.

Fixes #33160.

Change-Id: I36932c691693f0069e34384086d63133e249b06b
Reviewed-on: https://go-review.googlesource.com/c/go/+/264837
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
src/cmd/compile/internal/gc/inl.go
src/cmd/compile/internal/gc/main.go
test/escape_iface.go

index 098c0c99d5ed1a86753ba836a4c8fa273838da50..c35691bfd2143ab15cd6ed02b5de3bc9884ce55e 100644 (file)
@@ -1418,3 +1418,56 @@ func pruneUnusedAutos(ll []*Node, vis *hairyVisitor) []*Node {
        }
        return s
 }
+
+// devirtualize replaces interface method calls within fn with direct
+// concrete-type method calls where applicable.
+func devirtualize(fn *Node) {
+       Curfn = fn
+       inspectList(fn.Nbody, func(n *Node) bool {
+               if n.Op == OCALLINTER {
+                       devirtualizeCall(n)
+               }
+               return true
+       })
+}
+
+func devirtualizeCall(call *Node) {
+       recv := staticValue(call.Left.Left)
+       if recv.Op != OCONVIFACE {
+               return
+       }
+
+       typ := recv.Left.Type
+       if typ.IsInterface() {
+               return
+       }
+
+       if Debug.m != 0 {
+               Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ)
+       }
+
+       x := nodl(call.Left.Pos, ODOTTYPE, call.Left.Left, nil)
+       x.Type = typ
+       x = nodlSym(call.Left.Pos, OXDOT, x, call.Left.Sym)
+       x = typecheck(x, ctxExpr|ctxCallee)
+       if x.Op != ODOTMETH {
+               Fatalf("devirtualization failed: %v", x)
+       }
+       call.Op = OCALLMETH
+       call.Left = x
+
+       // Duplicated logic from typecheck for function call return
+       // value types.
+       //
+       // Receiver parameter size may have changed; need to update
+       // call.Type to get correct stack offsets for result
+       // parameters.
+       checkwidth(x.Type)
+       switch ft := x.Type; ft.NumResults() {
+       case 0:
+       case 1:
+               call.Type = ft.Results().Field(0).Type
+       default:
+               call.Type = ft.Results()
+       }
+}
index 4b401f2aa46eaab05c00b0fc510b899d266dd617..8b94c7f71b6d3b5645b22add049a1acb40ce8f6b 100644 (file)
@@ -701,6 +701,13 @@ func Main(archInit func(*Arch)) {
                })
        }
 
+       for _, n := range xtop {
+               if n.Op == ODCLFUNC {
+                       devirtualize(n)
+               }
+       }
+       Curfn = nil
+
        // Phase 6: Escape analysis.
        // Required for moving heap allocations onto stack,
        // which in turn is required by the closure implementation,
index 7b0914cadbbd006da0e5522d1e4b435a33b5a700..5a232fdbd4cd8fa4f69e498d6a9efb828c523b3b 100644 (file)
@@ -58,11 +58,10 @@ func efaceEscape0() {
                sink = v1
        }
        {
-               i := 0 // ERROR "moved to heap: i"
+               i := 0
                v := M0{&i}
-               // BAD: v does not escape to heap here
                var x M = v
-               x.M()
+               x.M() // ERROR "devirtualizing x.M"
        }
        {
                i := 0 // ERROR "moved to heap: i"
@@ -115,11 +114,10 @@ func efaceEscape1() {
                sink = v1 // ERROR "v1 escapes to heap"
        }
        {
-               i := 0 // ERROR "moved to heap: i"
+               i := 0
                v := M1{&i, 0}
-               // BAD: v does not escape to heap here
-               var x M = v // ERROR "v escapes to heap"
-               x.M()
+               var x M = v // ERROR "v does not escape"
+               x.M()       // ERROR "devirtualizing x.M"
        }
        {
                i := 0 // ERROR "moved to heap: i"
@@ -189,11 +187,10 @@ func efaceEscape2() {
                _ = ok
        }
        {
-               i := 0       // ERROR "moved to heap: i"
-               v := &M2{&i} // ERROR "&M2{...} escapes to heap"
-               // BAD: v does not escape to heap here
+               i := 0
+               v := &M2{&i} // ERROR "&M2{...} does not escape"
                var x M = v
-               x.M()
+               x.M() // ERROR "devirtualizing x.M"
        }
        {
                i := 0       // ERROR "moved to heap: i"