cmd/compile: check method name in PGO devirtualization

Currently, we devirtualize an interface call if the profile
indicates a concrete callee is hot on the same line, and the
concrete receiver implements the interface. But it is possible
that (likely due to another call on the same line, or possibly a
stale profile) the concrete call is to a different method.

With the current AST construction we generate correct code, as we
extract the method name from the interface call and use that to
create the concrete call. But the devirtualization decision is
based on an unrelated call in the profile.

Check the method name when finding the hottest callee, so we won't
use unrelated calls to different methods.

Change-Id: I75c026997926f21bd6cc5266d3ffe99649a9b2d9
Reviewed-on: https://go-review.googlesource.com/c/go/+/500961
Run-TryBot: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
Cherry Mui 2023-06-05 22:28:48 -04:00
parent db3f952b1f
commit fd03e6ebc9
1 changed files with 15 additions and 5 deletions

View File

@ -15,6 +15,7 @@ import (
"encoding/json"
"fmt"
"os"
"strings"
)
// CallStat summarizes a single call site.
@ -396,9 +397,9 @@ func methodRecvType(fn *ir.Func) *types.Type {
return recv.Type
}
// interfaceCallRecvType returns the type of the interface used in an interface
// call.
func interfaceCallRecvType(call *ir.CallExpr) *types.Type {
// interfaceCallRecvTypeAndMethod returns the type and the method of the interface
// used in an interface call.
func interfaceCallRecvTypeAndMethod(call *ir.CallExpr) (*types.Type, *types.Sym) {
if call.Op() != ir.OCALLINTER {
base.Fatalf("Call isn't OCALLINTER: %+v", call)
}
@ -408,7 +409,7 @@ func interfaceCallRecvType(call *ir.CallExpr) *types.Type {
base.Fatalf("OCALLINTER doesn't contain SelectorExpr: %+v", call)
}
return sel.X.Type()
return sel.X.Type(), sel.Sel
}
// findHotConcreteCallee returns the *ir.Func of the hottest callee of an
@ -418,7 +419,7 @@ func findHotConcreteCallee(p *pgo.Profile, caller *ir.Func, call *ir.CallExpr) (
callerNode := p.WeightedCG.IRNodes[callerName]
callOffset := pgo.NodeLineOffset(call, caller)
inter := interfaceCallRecvType(call)
inter, method := interfaceCallRecvTypeAndMethod(call)
var hottest *pgo.IREdge
@ -512,6 +513,15 @@ func findHotConcreteCallee(p *pgo.Profile, caller *ir.Func, call *ir.CallExpr) (
continue
}
// If the method name is different it is most likely from a
// different call on the same line
if !strings.HasSuffix(e.Dst.Name(), "."+method.Name) {
if base.Debug.PGODebug >= 2 {
fmt.Printf("%v: edge %s:%d -> %s (weight %d): callee is a different method\n", ir.Line(call), callerName, callOffset, e.Dst.Name(), e.Weight)
}
continue
}
if base.Debug.PGODebug >= 2 {
fmt.Printf("%v: edge %s:%d -> %s (weight %d): hottest so far\n", ir.Line(call), callerName, callOffset, e.Dst.Name(), e.Weight)
}