Skip to content

Commit 3637132

Browse files
thepuddscherrymui
authored andcommitted
cmd/compile/internal/devirtualize: devirtualize methods in other packages if current package has a concrete reference
The new PGO-driven indirect call specialization from CL 492436 in theory should allow for devirtualization on methods in another package when those methods are directly referenced in the current package. However, inline.InlineImpossible was checking for a zero-length fn.Body and would cause devirtualization to fail with a debug log message like: "should not PGO devirtualize (*Speaker1).Speak: no function body" Previously, the logic in inline.InlineImpossible was only called on local functions, but with PGO-based devirtualization, it can now be called on imported functions, where inlinable imported functions will have a zero-length fn.Body but a non-nil fn.Inl. We update inline.InlineImpossible to handle imported functions by adding a call to typecheck.HaveInlineBody in the check that was previously failing. For the test, we need to have a hopefully temporary workaround of adding explicit references to the callees in another package for devirtualization to work. CL 497175 or similar should enable removing this workaround. Fixes #60561 Updates #59959 Change-Id: I48449b7d8b329d84151bd3b506b8093c262eb2a3 GitHub-Last-Rev: 2d53c55 GitHub-Pull-Request: #60565 Reviewed-on: https://go-review.googlesource.com/c/go/+/500155 Run-TryBot: thepudds <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Reviewed-by: Michael Pratt <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 3fd867c commit 3637132

File tree

7 files changed

+62
-35
lines changed

7 files changed

+62
-35
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -357,11 +357,11 @@ func rewriteCondCall(call *ir.CallExpr, curfn, callee *ir.Func, concretetyp *typ
357357
elseBlock.Append(call)
358358
} else {
359359
// Copy slice so edits in one location don't affect another.
360-
thenRet := append([]ir.Node(nil), retvars...)
360+
thenRet := append([]ir.Node(nil), retvars...)
361361
thenAsList := ir.NewAssignListStmt(pos, ir.OAS2, thenRet, []ir.Node{concreteCall})
362362
thenBlock.Append(typecheck.Stmt(thenAsList))
363363

364-
elseRet := append([]ir.Node(nil), retvars...)
364+
elseRet := append([]ir.Node(nil), retvars...)
365365
elseAsList := ir.NewAssignListStmt(pos, ir.OAS2, elseRet, []ir.Node{call})
366366
elseBlock.Append(typecheck.Stmt(elseAsList))
367367
}

src/cmd/compile/internal/inline/inl.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -414,8 +414,9 @@ func InlineImpossible(fn *ir.Func) string {
414414
return reason
415415
}
416416

417-
// If fn has no body (is defined outside of Go), cannot inline it.
418-
if len(fn.Body) == 0 {
417+
// If a local function has no fn.Body (is defined outside of Go), cannot inline it.
418+
// Imported functions don't have fn.Body but might have inline body in fn.Inl.
419+
if len(fn.Body) == 0 && !typecheck.HaveInlineBody(fn) {
419420
reason = "no function body"
420421
return reason
421422
}

src/cmd/compile/internal/test/pgo_devirtualize_test.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ go 1.19
5757

5858
want := []devirtualization{
5959
{
60-
pos: "./devirt.go:81:21",
61-
callee: "Mult.Multiply",
60+
pos: "./devirt.go:61:21",
61+
callee: "mult.Mult.Multiply",
6262
},
6363
{
64-
pos: "./devirt.go:81:31",
64+
pos: "./devirt.go:61:31",
6565
callee: "Add.Add",
6666
},
6767
}
@@ -115,8 +115,10 @@ func TestPGODevirtualize(t *testing.T) {
115115

116116
// Copy the module to a scratch location so we can add a go.mod.
117117
dir := t.TempDir()
118-
119-
for _, file := range []string{"devirt.go", "devirt_test.go", "devirt.pprof"} {
118+
if err := os.Mkdir(filepath.Join(dir, "mult"), 0755); err != nil {
119+
t.Fatalf("error creating dir: %v", err)
120+
}
121+
for _, file := range []string{"devirt.go", "devirt_test.go", "devirt.pprof", filepath.Join("mult", "mult.go")} {
120122
if err := copyFile(filepath.Join(dir, file), filepath.Join(srcDir, file)); err != nil {
121123
t.Fatalf("error copying %s: %v", file, err)
122124
}

src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.go

+14-24
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,12 @@
1111

1212
package devirt
1313

14-
type Multiplier interface {
15-
Multiply(a, b int) int
16-
}
17-
18-
type Adder interface {
19-
Add(a, b int) int
20-
}
14+
import "example.com/pgo/devirtualize/mult"
2115

2216
var sink int
2317

24-
type Mult struct{}
25-
26-
func (Mult) Multiply(a, b int) int {
27-
for i := 0; i < 1000; i++ {
28-
sink++
29-
}
30-
return a * b
31-
}
32-
33-
type NegMult struct{}
34-
35-
func (NegMult) Multiply(a, b int) int {
36-
for i := 0; i < 1000; i++ {
37-
sink++
38-
}
39-
return -1 * a * b
18+
type Adder interface {
19+
Add(a, b int) int
4020
}
4121

4222
type Add struct{}
@@ -60,7 +40,7 @@ func (Sub) Add(a, b int) int {
6040
// Exercise calls mostly a1 and m1.
6141
//
6242
//go:noinline
63-
func Exercise(iter int, a1, a2 Adder, m1, m2 Multiplier) {
43+
func Exercise(iter int, a1, a2 Adder, m1, m2 mult.Multiplier) {
6444
for i := 0; i < iter; i++ {
6545
a := a1
6646
m := m1
@@ -81,3 +61,13 @@ func Exercise(iter int, a1, a2 Adder, m1, m2 Multiplier) {
8161
sink += m.Multiply(42, a.Add(1, 2))
8262
}
8363
}
64+
65+
func init() {
66+
// TODO: until https://golang.org/cl/497175 or similar lands,
67+
// we need to create an explicit reference to callees
68+
// in another package for devirtualization to work.
69+
m := mult.Mult{}
70+
m.Multiply(42, 0)
71+
n := mult.NegMult{}
72+
n.Multiply(42, 0)
73+
}
Binary file not shown.

src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ package devirt
1313

1414
import (
1515
"testing"
16+
17+
"example.com/pgo/devirtualize/mult"
1618
)
1719

1820
func BenchmarkDevirt(b *testing.B) {
1921
var (
2022
a1 Add
2123
a2 Sub
22-
m1 Mult
23-
m2 NegMult
24+
m1 mult.Mult
25+
m2 mult.NegMult
2426
)
2527

2628
Exercise(b.N, a1, a2, m1, m2)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// WARNING: Please avoid updating this file.
6+
// See the warning in ../devirt.go for more details.
7+
8+
package mult
9+
10+
var sink int
11+
12+
type Multiplier interface {
13+
Multiply(a, b int) int
14+
}
15+
16+
type Mult struct{}
17+
18+
func (Mult) Multiply(a, b int) int {
19+
for i := 0; i < 1000; i++ {
20+
sink++
21+
}
22+
return a * b
23+
}
24+
25+
type NegMult struct{}
26+
27+
func (NegMult) Multiply(a, b int) int {
28+
for i := 0; i < 1000; i++ {
29+
sink++
30+
}
31+
return -1 * a * b
32+
}

0 commit comments

Comments
 (0)