1
0
mirror of https://github.com/golang/go synced 2024-11-20 08:14:41 -07:00

cmd/compile: collect reasons in inlining test

If we use -gcflags='-m -m', the compiler should give us a reason why a
func couldn't be inlined. Add the extra -m necessary for that extra info
and use it to give better test failures. For example, for the func in
the TODO:

	--- FAIL: TestIntendedInlining (1.53s)
		inl_test.go:104: runtime.nextFreeFast was not inlined: function too complex

We might increase the number of -m flags to get more information at some
later point, such as getting details on how close the func was to the
inlining budget.

Also started using regexes, as the output parsing is getting a bit too
complex for manual string handling.

While at it, also refactored the test to not buffer the entire output
into memory. This is fine in practice, but it won't scale well as we add
more packages or we depend more on the compiler's debugging output.

For example, "go build -a -gcflags='-m -m' std" prints nearly 40MB of
plaintext - and we only need to see the output line by line anyway.

Updates #21851.

Change-Id: I00986ff360eb56e4e9737b65a6be749ef8540643
Reviewed-on: https://go-review.googlesource.com/63810
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Daniel Martí 2017-09-14 15:51:18 +01:00
parent 7a5d76fa62
commit 3d741349f5

View File

@ -5,10 +5,13 @@
package gc package gc
import ( import (
"bytes" "bufio"
"internal/testenv" "internal/testenv"
"io"
"os/exec" "os/exec"
"regexp"
"runtime" "runtime"
"strings"
"testing" "testing"
) )
@ -51,37 +54,57 @@ func TestIntendedInlining(t *testing.T) {
want["runtime"] = append(want["runtime"], "nextFreeFast") want["runtime"] = append(want["runtime"], "nextFreeFast")
} }
m := make(map[string]bool) notInlinedReason := make(map[string]string)
pkgs := make([]string, 0, len(want)) pkgs := make([]string, 0, len(want))
for pname, fnames := range want { for pname, fnames := range want {
pkgs = append(pkgs, pname) pkgs = append(pkgs, pname)
for _, fname := range fnames { for _, fname := range fnames {
m[pname+"."+fname] = true notInlinedReason[pname+"."+fname] = "unknown reason"
} }
} }
args := append([]string{"build", "-a", "-gcflags=-m"}, pkgs...) args := append([]string{"build", "-a", "-gcflags=-m -m"}, pkgs...)
cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), args...)) cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), args...))
out, err := cmd.CombinedOutput() pr, pw := io.Pipe()
if err != nil { cmd.Stdout = pw
t.Logf("%s", out) cmd.Stderr = pw
t.Fatal(err) cmdErr := make(chan error, 1)
} go func() {
lines := bytes.Split(out, []byte{'\n'}) cmdErr <- cmd.Run()
pw.Close()
}()
scanner := bufio.NewScanner(pr)
curPkg := "" curPkg := ""
for _, l := range lines { canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
if bytes.HasPrefix(l, []byte("# ")) { cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
curPkg = string(l[2:]) for scanner.Scan() {
} line := scanner.Text()
f := bytes.Split(l, []byte(": can inline ")) if strings.HasPrefix(line, "# ") {
if len(f) < 2 { curPkg = line[2:]
continue
}
if m := canInline.FindStringSubmatch(line); m != nil {
fname := m[1]
delete(notInlinedReason, curPkg+"."+fname)
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
}
continue continue
} }
fn := bytes.TrimSpace(f[1])
delete(m, curPkg+"."+string(fn))
} }
if err := <-cmdErr; err != nil {
for s := range m { t.Fatal(err)
t.Errorf("function %s not inlined", s) }
if err := scanner.Err(); err != nil {
t.Fatal(err)
}
for fullName, reason := range notInlinedReason {
t.Errorf("%s was not inlined: %s", fullName, reason)
} }
} }