Skip to content

Commit 8c445b7

Browse files
prattmicgopherbot
authored andcommitted
cmd/compile: enable PGO-driven call devirtualization
This CL is originally based on CL 484838 from [email protected]. Add a new PGO-based devirtualize pass. This pass conditionally devirtualizes interface calls for the hottest callee. That is, it performs a transformation like: type Iface interface { Foo() } type Concrete struct{} func (Concrete) Foo() {} func foo(i Iface) { i.Foo() } to: func foo(i Iface) { if c, ok := i.(Concrete); ok { c.Foo() } else { i.Foo() } } The primary benefit of this transformation is enabling inlining of the direct calls. Today this change has no impact on the escape behavior, as the fallback interface always forces an escape. But improving escape analysis to take advantage of this is an area of potential work. This CL is the bare minimum of a devirtualization implementation. There are still numerous limitations: * Callees not directly referenced in the current package can be missed (even if they are in the transitive dependences). * Callees not in the transitive dependencies of the current package are missed. * Only interface method calls are supported, not other indirect function calls. * Multiple calls to compatible interfaces on the same line cannot be distinguished and will use the same callee target. * Callees that only partially implement an interface (they are embedded in another type that completes the interface) cannot be devirtualized. * Others, mentioned in TODOs. Fixes #59959 Change-Id: I8bedb516139695ee4069650b099d05957b7ce5ee Reviewed-on: https://go-review.googlesource.com/c/go/+/492436 Reviewed-by: Cherry Mui <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]> Run-TryBot: Michael Pratt <[email protected]> Auto-Submit: Michael Pratt <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 6761bff commit 8c445b7

File tree

11 files changed

+1049
-122
lines changed

11 files changed

+1049
-122
lines changed

src/cmd/compile/internal/base/debug.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type DebugFlags struct {
5454
PGOInline int `help:"enable profile-guided inlining" concurrent:"ok"`
5555
PGOInlineCDFThreshold string `help:"cumulative threshold percentage for determining call sites as hot candidates for inlining" concurrent:"ok"`
5656
PGOInlineBudget int `help:"inline budget for hot functions" concurrent:"ok"`
57+
PGODevirtualize int `help:"enable profile-guided devirtualization" concurrent:"ok"`
5758
WrapGlobalMapDbg int `help:"debug trace output for global map init wrapping"`
5859
WrapGlobalMapCtl int `help:"global map init wrap control (0 => default, 1 => off, 2 => stress mode, no size cutoff)"`
5960

src/cmd/compile/internal/base/flag.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ func ParseFlags() {
169169
Debug.InlFuncsWithClosures = 1
170170
Debug.InlStaticInit = 1
171171
Debug.PGOInline = 1
172+
Debug.PGODevirtualize = 1
172173
Debug.SyncFrames = -1 // disable sync markers by default
173174

174175
Debug.Checkptr = -1 // so we can tell whether it is set explicitly

src/cmd/compile/internal/devirtualize/devirtualize.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// Package devirtualize implements a simple "devirtualization"
6-
// optimization pass, which replaces interface method calls with
7-
// direct concrete-type method calls where possible.
5+
// Package devirtualize implements two "devirtualization" optimization passes:
6+
//
7+
// - "Static" devirtualization which replaces interface method calls with
8+
// direct concrete-type method calls where possible.
9+
// - "Profile-guided" devirtualization which replaces indirect calls with a
10+
// conditional direct call to the hottest concrete callee from a profile, as
11+
// well as a fallback using the original indirect call.
812
package devirtualize
913

1014
import (
@@ -14,8 +18,9 @@ import (
1418
"cmd/compile/internal/types"
1519
)
1620

17-
// Func devirtualizes calls within fn where possible.
18-
func Func(fn *ir.Func) {
21+
// Static devirtualizes calls within fn where possible when the concrete callee
22+
// is available statically.
23+
func Static(fn *ir.Func) {
1924
ir.CurFunc = fn
2025

2126
// For promoted methods (including value-receiver methods promoted to pointer-receivers),
@@ -34,14 +39,15 @@ func Func(fn *ir.Func) {
3439
return
3540
case *ir.CallExpr:
3641
if !goDeferCall[n] {
37-
Call(n)
42+
staticCall(n)
3843
}
3944
}
4045
})
4146
}
4247

43-
// Call devirtualizes the given call if possible.
44-
func Call(call *ir.CallExpr) {
48+
// staticCall devirtualizes the given call if possible when the concrete callee
49+
// is available statically.
50+
func staticCall(call *ir.CallExpr) {
4551
if call.Op() != ir.OCALLINTER {
4652
return
4753
}

0 commit comments

Comments
 (0)