mirror of
https://github.com/golang/go
synced 2024-09-15 22:20:06 +00:00
cmd/compile: early devirtualization of interface method calls
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>
This commit is contained in:
parent
f2c0c2b902
commit
5cc43c51c9
|
@ -1418,3 +1418,56 @@ func pruneUnusedAutos(ll []*Node, vis *hairyVisitor) []*Node {
|
||||||
}
|
}
|
||||||
return s
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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.
|
// Phase 6: Escape analysis.
|
||||||
// Required for moving heap allocations onto stack,
|
// Required for moving heap allocations onto stack,
|
||||||
// which in turn is required by the closure implementation,
|
// which in turn is required by the closure implementation,
|
||||||
|
|
|
@ -58,11 +58,10 @@ func efaceEscape0() {
|
||||||
sink = v1
|
sink = v1
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
i := 0 // ERROR "moved to heap: i"
|
i := 0
|
||||||
v := M0{&i}
|
v := M0{&i}
|
||||||
// BAD: v does not escape to heap here
|
|
||||||
var x M = v
|
var x M = v
|
||||||
x.M()
|
x.M() // ERROR "devirtualizing x.M"
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
i := 0 // ERROR "moved to heap: i"
|
i := 0 // ERROR "moved to heap: i"
|
||||||
|
@ -115,11 +114,10 @@ func efaceEscape1() {
|
||||||
sink = v1 // ERROR "v1 escapes to heap"
|
sink = v1 // ERROR "v1 escapes to heap"
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
i := 0 // ERROR "moved to heap: i"
|
i := 0
|
||||||
v := M1{&i, 0}
|
v := M1{&i, 0}
|
||||||
// BAD: v does not escape to heap here
|
var x M = v // ERROR "v does not escape"
|
||||||
var x M = v // ERROR "v escapes to heap"
|
x.M() // ERROR "devirtualizing x.M"
|
||||||
x.M()
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
i := 0 // ERROR "moved to heap: i"
|
i := 0 // ERROR "moved to heap: i"
|
||||||
|
@ -189,11 +187,10 @@ func efaceEscape2() {
|
||||||
_ = ok
|
_ = ok
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
i := 0 // ERROR "moved to heap: i"
|
i := 0
|
||||||
v := &M2{&i} // ERROR "&M2{...} escapes to heap"
|
v := &M2{&i} // ERROR "&M2{...} does not escape"
|
||||||
// BAD: v does not escape to heap here
|
|
||||||
var x M = v
|
var x M = v
|
||||||
x.M()
|
x.M() // ERROR "devirtualizing x.M"
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
i := 0 // ERROR "moved to heap: i"
|
i := 0 // ERROR "moved to heap: i"
|
||||||
|
|
Loading…
Reference in a new issue