const preProfFileName = "devirt.pprof.node_map"
// testPGODevirtualize tests that specific PGO devirtualize rewrites are performed.
-func testPGODevirtualize(t *testing.T, dir string, want []devirtualization, pgoProfileName string) {
+func testPGODevirtualize(t *testing.T, dir string, want, nowant []devirtualization, pgoProfileName string) {
testenv.MustHaveGoRun(t)
t.Parallel()
}
got := make(map[devirtualization]struct{})
+ gotNoHot := make(map[devirtualization]struct{})
devirtualizedLine := regexp.MustCompile(`(.*): PGO devirtualizing \w+ call .* to (.*)`)
+ noHotLine := regexp.MustCompile(`(.*): call .*: no hot callee`)
scanner := bufio.NewScanner(pr)
for scanner.Scan() {
t.Logf("child: %s", line)
m := devirtualizedLine.FindStringSubmatch(line)
- if m == nil {
+ if m != nil {
+ d := devirtualization{
+ pos: m[1],
+ callee: m[2],
+ }
+ got[d] = struct{}{}
continue
}
-
- d := devirtualization{
- pos: m[1],
- callee: m[2],
+ m = noHotLine.FindStringSubmatch(line)
+ if m != nil {
+ d := devirtualization{
+ pos: m[1],
+ }
+ gotNoHot[d] = struct{}{}
}
- got[d] = struct{}{}
}
if err := cmd.Wait(); err != nil {
t.Fatalf("error running go test: %v", err)
}
t.Errorf("devirtualization %v missing; got %v", w, got)
}
+ for _, nw := range nowant {
+ if _, ok := gotNoHot[nw]; !ok {
+ t.Errorf("unwanted devirtualization %v; got %v", nw, got)
+ }
+ }
// Run test with PGO to ensure the assertions are still true.
cmd = testenv.CleanCmdEnv(testenv.Command(t, out))
// callee: "mult.MultClosure.func1",
//},
}
+ nowant := []devirtualization{
+ // ExerciseIfaceZeroWeight
+ {
+ pos: "./devirt.go:256:29",
+ },
+ // ExerciseIndirCallZeroWeight
+ {
+ pos: "./devirt.go:282:37",
+ },
+ }
- testPGODevirtualize(t, dir, want, profFileName)
+ testPGODevirtualize(t, dir, want, nowant, profFileName)
}
// TestPGOPreprocessDevirtualize tests that specific functions are devirtualized when PGO
// callee: "mult.MultClosure.func1",
//},
}
+ nowant := []devirtualization{
+ // ExerciseIfaceZeroWeight
+ {
+ pos: "./devirt.go:256:29",
+ },
+ // ExerciseIndirCallZeroWeight
+ {
+ pos: "./devirt.go:282:37",
+ },
+ }
- testPGODevirtualize(t, dir, want, preProfFileName)
+ testPGODevirtualize(t, dir, want, nowant, preProfFileName)
}
// Regression test for https://go.dev/issue/65615. If a target function changes
// callee: "mult.MultClosure.func1",
//},
}
+ nowant := []devirtualization{
+ // ExerciseIfaceZeroWeight
+ {
+ pos: "./devirt.go:256:29",
+ },
+ // ExerciseIndirCallZeroWeight
+ {
+ pos: "./devirt.go:282:37",
+ },
+ }
- testPGODevirtualize(t, dir, want, profFileName)
+ testPGODevirtualize(t, dir, want, nowant, profFileName)
}
var multFnRe = regexp.MustCompile(`func MultFn\(a, b int64\) int64`)
}
return val
}
+
+//go:noinline
+func IfaceZeroWeight(a *Add, b Adder) bool {
+ return a.Add(1, 2) == b.Add(3, 4) // unwanted devirtualization
+}
+
+// ExerciseIfaceZeroWeight never calls IfaceZeroWeight, so the callee
+// is not expected to appear in the profile.
+//
+//go:noinline
+func ExerciseIfaceZeroWeight() {
+ if false {
+ a := &Add{}
+ b := &Sub{}
+ // Unreachable call
+ IfaceZeroWeight(a, b)
+ }
+}
+
+func DirectCall() bool {
+ return true
+}
+
+func IndirectCall() bool {
+ return false
+}
+
+//go:noinline
+func IndirCallZeroWeight(indirectCall func() bool) bool {
+ return DirectCall() && indirectCall() // unwanted devirtualization
+}
+
+// ExerciseIndirCallZeroWeight never calls IndirCallZeroWeight, so the
+// callee is not expected to appear in the profile.
+//
+//go:noinline
+func ExerciseIndirCallZeroWeight() {
+ if false {
+ // Unreachable call
+ IndirCallZeroWeight(IndirectCall)
+ }
+}