2022-09-09 11:29:32 -07:00
|
|
|
// Copyright 2017 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"fmt"
|
|
|
|
|
"internal/testenv"
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
2022-10-28 14:13:59 -04:00
|
|
|
"path/filepath"
|
2022-09-09 11:29:32 -07:00
|
|
|
"regexp"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// TestPGOIntendedInlining tests that specific functions are inlined.
|
|
|
|
|
func TestPGOIntendedInlining(t *testing.T) {
|
|
|
|
|
testenv.MustHaveGoRun(t)
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
2022-10-28 14:13:59 -04:00
|
|
|
const pkg = "cmd/compile/internal/test/testdata/pgo/inline"
|
2022-09-09 11:29:32 -07:00
|
|
|
|
2022-10-28 14:13:59 -04:00
|
|
|
want := []string{
|
|
|
|
|
"(*BS).NS",
|
2022-09-09 11:29:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The functions which are not expected to be inlined are as follows.
|
2022-10-28 14:13:59 -04:00
|
|
|
wantNot := []string{
|
|
|
|
|
// The calling edge main->A is hot and the cost of A is large
|
|
|
|
|
// than inlineHotCalleeMaxBudget.
|
|
|
|
|
"A",
|
|
|
|
|
// The calling edge BenchmarkA" -> benchmarkB is cold and the
|
|
|
|
|
// cost of A is large than inlineMaxBudget.
|
|
|
|
|
"benchmarkB",
|
2022-09-09 11:29:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
must := map[string]bool{
|
|
|
|
|
"(*BS).NS": true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
notInlinedReason := make(map[string]string)
|
2022-10-28 14:13:59 -04:00
|
|
|
for _, fname := range want {
|
|
|
|
|
fullName := pkg + "." + fname
|
|
|
|
|
if _, ok := notInlinedReason[fullName]; ok {
|
|
|
|
|
t.Errorf("duplicate func: %s", fullName)
|
2022-09-09 11:29:32 -07:00
|
|
|
}
|
2022-10-28 14:13:59 -04:00
|
|
|
notInlinedReason[fullName] = "unknown reason"
|
2022-09-09 11:29:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the compiler emit "cannot inline for function A", the entry A
|
|
|
|
|
// in expectedNotInlinedList will be removed.
|
|
|
|
|
expectedNotInlinedList := make(map[string]struct{})
|
2022-10-28 14:13:59 -04:00
|
|
|
for _, fname := range wantNot {
|
|
|
|
|
fullName := pkg + "." + fname
|
|
|
|
|
expectedNotInlinedList[fullName] = struct{}{}
|
2022-09-09 11:29:32 -07:00
|
|
|
}
|
|
|
|
|
|
2022-10-28 14:13:59 -04:00
|
|
|
// go test -c -o /tmp/test.exe -cpuprofile testdata/pgo/inline/inline_hot.pprof cmd/compile/internal/test/testdata/pgo/inline
|
|
|
|
|
curdir, err := os.Getwd()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("error getting wd: %v", err)
|
2022-09-09 11:29:32 -07:00
|
|
|
}
|
2022-10-28 14:13:59 -04:00
|
|
|
gcflag := fmt.Sprintf("-gcflags=-m -m -pgoprofile %s/testdata/pgo/inline/inline_hot.pprof", curdir)
|
|
|
|
|
out := filepath.Join(t.TempDir(), "test.exe")
|
|
|
|
|
cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "test", "-c", "-o", out, gcflag, pkg))
|
2022-09-09 11:29:32 -07:00
|
|
|
|
2022-10-28 14:13:59 -04:00
|
|
|
pr, pw, err := os.Pipe()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("error creating pipe: %v", err)
|
|
|
|
|
}
|
|
|
|
|
defer pr.Close()
|
2022-09-09 11:29:32 -07:00
|
|
|
cmd.Stdout = pw
|
|
|
|
|
cmd.Stderr = pw
|
2022-10-28 14:13:59 -04:00
|
|
|
|
|
|
|
|
err = cmd.Start()
|
|
|
|
|
pw.Close()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("error starting go test: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 11:29:32 -07:00
|
|
|
scanner := bufio.NewScanner(pr)
|
|
|
|
|
curPkg := ""
|
|
|
|
|
canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
|
|
|
|
|
haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
|
|
|
|
|
cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
|
line := scanner.Text()
|
2022-10-28 14:13:59 -04:00
|
|
|
t.Logf("child: %s", line)
|
2022-09-09 11:29:32 -07:00
|
|
|
if strings.HasPrefix(line, "# ") {
|
|
|
|
|
curPkg = line[2:]
|
|
|
|
|
splits := strings.Split(curPkg, " ")
|
|
|
|
|
curPkg = splits[0]
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if m := haveInlined.FindStringSubmatch(line); m != nil {
|
|
|
|
|
fname := m[1]
|
|
|
|
|
delete(notInlinedReason, curPkg+"."+fname)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if m := canInline.FindStringSubmatch(line); m != nil {
|
|
|
|
|
fname := m[1]
|
|
|
|
|
fullname := curPkg + "." + fname
|
|
|
|
|
// If function must be inlined somewhere, being inlinable is not enough
|
|
|
|
|
if _, ok := must[fullname]; !ok {
|
|
|
|
|
delete(notInlinedReason, fullname)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if m := cannotInline.FindStringSubmatch(line); m != nil {
|
|
|
|
|
fname, reason := m[1], m[2]
|
|
|
|
|
fullName := curPkg + "." + fname
|
|
|
|
|
if _, ok := notInlinedReason[fullName]; ok {
|
|
|
|
|
// cmd/compile gave us a reason why
|
|
|
|
|
notInlinedReason[fullName] = reason
|
|
|
|
|
}
|
|
|
|
|
delete(expectedNotInlinedList, fullName)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-28 14:13:59 -04:00
|
|
|
if err := cmd.Wait(); err != nil {
|
|
|
|
|
t.Fatalf("error running go test: %v", err)
|
2022-09-09 11:29:32 -07:00
|
|
|
}
|
|
|
|
|
if err := scanner.Err(); err != nil {
|
2022-10-28 14:13:59 -04:00
|
|
|
t.Fatalf("error reading go test output: %v", err)
|
2022-09-09 11:29:32 -07:00
|
|
|
}
|
|
|
|
|
for fullName, reason := range notInlinedReason {
|
|
|
|
|
t.Errorf("%s was not inlined: %s", fullName, reason)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the list expectedNotInlinedList is not empty, it indicates
|
|
|
|
|
// the functions in the expectedNotInlinedList are marked with caninline.
|
|
|
|
|
for fullName, _ := range expectedNotInlinedList {
|
|
|
|
|
t.Errorf("%s was expected not inlined", fullName)
|
|
|
|
|
}
|
|
|
|
|
}
|