cmd/compile: enable flag-specified dump of specific phase+function

For very large input files, use of GOSSAFUNC to obtain a dump
after compilation steps can lead to both unwieldy large output
files and unwieldy larger processes (because the output is
buffered in a string).  This flag

  -d=ssa/<phase>/dump:<function name>

provides finer control of what is dumped, into a smaller
file, and with less memory overhead in the running compiler.
The special phase name "build" is added to allow printing
of the just-built ssa before any transformations are applied.

This was helpful in making sense of the gogo/protobuf
problems.

The output format was tweaked to remove gratuitous spaces,
and a crude -d=ssa/help help text was added.

Change-Id: If7516e22203420eb6ed3614f7cee44cb9260f43e
Reviewed-on: https://go-review.googlesource.com/23044
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
David Chase 2016-05-11 15:25:17 -04:00
parent 10560afb54
commit a190f3c8a3
3 changed files with 99 additions and 8 deletions

View File

@ -246,6 +246,7 @@ func Main() {
continue continue
} }
val := 1 val := 1
valstring := ""
if i := strings.Index(name, "="); i >= 0 { if i := strings.Index(name, "="); i >= 0 {
var err error var err error
val, err = strconv.Atoi(name[i+1:]) val, err = strconv.Atoi(name[i+1:])
@ -253,6 +254,9 @@ func Main() {
log.Fatalf("invalid debug value %v", name) log.Fatalf("invalid debug value %v", name)
} }
name = name[:i] name = name[:i]
} else if i := strings.Index(name, ":"); i >= 0 {
valstring = name[i+1:]
name = name[:i]
} }
for _, t := range debugtab { for _, t := range debugtab {
if t.name == name { if t.name == name {
@ -273,7 +277,7 @@ func Main() {
flag = phase[i+1:] flag = phase[i+1:]
phase = phase[:i] phase = phase[:i]
} }
err := ssa.PhaseOption(phase, flag, val) err := ssa.PhaseOption(phase, flag, val, valstring)
if err != "" { if err != "" {
log.Fatalf(err) log.Fatalf(err)
} }

View File

@ -7,6 +7,7 @@ package ssa
import ( import (
"fmt" "fmt"
"log" "log"
"os"
"regexp" "regexp"
"runtime" "runtime"
"strings" "strings"
@ -41,6 +42,9 @@ func Compile(f *Func) {
// Run all the passes // Run all the passes
printFunc(f) printFunc(f)
f.Config.HTML.WriteFunc("start", f) f.Config.HTML.WriteFunc("start", f)
if BuildDump != "" && BuildDump == f.Name {
f.dumpFile("build")
}
if checkEnabled { if checkEnabled {
checkFunc(f) checkFunc(f)
} }
@ -96,6 +100,10 @@ func Compile(f *Func) {
f.LogStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs) f.LogStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs)
} }
} }
if p.dump != nil && p.dump[f.Name] {
// Dump function to appropriately named file
f.dumpFile(phaseName)
}
if checkEnabled { if checkEnabled {
checkFunc(f) checkFunc(f)
} }
@ -105,16 +113,48 @@ func Compile(f *Func) {
phaseName = "" phaseName = ""
} }
// TODO: should be a config field
var dumpFileSeq int
// dumpFile creates a file from the phase name and function name
// Dumping is done to files to avoid buffering huge strings before
// output.
func (f *Func) dumpFile(phaseName string) {
dumpFileSeq++
fname := fmt.Sprintf("%s__%s_%d.dump", phaseName, f.Name, dumpFileSeq)
fname = strings.Replace(fname, " ", "_", -1)
fname = strings.Replace(fname, "/", "_", -1)
fname = strings.Replace(fname, ":", "_", -1)
fi, err := os.Create(fname)
if err != nil {
f.Config.Warnl(0, "Unable to create after-phase dump file %s", fname)
return
}
p := stringFuncPrinter{w: fi}
fprintFunc(p, f)
fi.Close()
}
type pass struct { type pass struct {
name string name string
fn func(*Func) fn func(*Func)
required bool required bool
disabled bool disabled bool
time bool // report time to run pass time bool // report time to run pass
mem bool // report mem stats to run pass mem bool // report mem stats to run pass
stats int // pass reports own "stats" (e.g., branches removed) stats int // pass reports own "stats" (e.g., branches removed)
debug int // pass performs some debugging. =1 should be in error-testing-friendly Warnl format. debug int // pass performs some debugging. =1 should be in error-testing-friendly Warnl format.
test int // pass-specific ad-hoc option, perhaps useful in development test int // pass-specific ad-hoc option, perhaps useful in development
dump map[string]bool // dump if function name matches
}
func (p *pass) addDump(s string) {
if p.dump == nil {
p.dump = make(map[string]bool)
}
p.dump[s] = true
} }
// Run consistency checker between each phase // Run consistency checker between each phase
@ -127,6 +167,7 @@ var IntrinsicsDisable bool
var BuildDebug int var BuildDebug int
var BuildTest int var BuildTest int
var BuildStats int var BuildStats int
var BuildDump string // name of function to dump after initial build of ssa
// PhaseOption sets the specified flag in the specified ssa phase, // PhaseOption sets the specified flag in the specified ssa phase,
// returning empty string if this was successful or a string explaining // returning empty string if this was successful or a string explaining
@ -146,7 +187,35 @@ var BuildStats int
// //
// BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash // BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash
// //
func PhaseOption(phase, flag string, val int) string { func PhaseOption(phase, flag string, val int, valString string) string {
if phase == "help" {
lastcr := 0
phasenames := "check, all, build, intrinsics"
for _, p := range passes {
pn := strings.Replace(p.name, " ", "_", -1)
if len(pn)+len(phasenames)-lastcr > 70 {
phasenames += "\n"
lastcr = len(phasenames)
phasenames += pn
} else {
phasenames += ", " + pn
}
}
return "" +
`GcFlag -d=ssa/<phase>/<flag>[=<value>]|[:<function_name>]
<phase> is one of:
` + phasenames + `
<flag> is one of on, off, debug, mem, time, test, stats, dump
<value> defaults to 1
<function_name> is required for "dump", specifies name of function to dump after <phase>
Except for dump, output is directed to standard out; dump appears in a file.
Phase "all" supports flags "time", "mem", and "dump".
Phases "intrinsics" supports flags "on", "off", and "debug".
Interpretation of the "debug" value depends on the phase.
Dump files are named <phase>__<function_name>_<seq>.dump.
`
}
if phase == "check" && flag == "on" { if phase == "check" && flag == "on" {
checkEnabled = val != 0 checkEnabled = val != 0
return "" return ""
@ -157,9 +226,18 @@ func PhaseOption(phase, flag string, val int) string {
} }
alltime := false alltime := false
allmem := false
alldump := false
if phase == "all" { if phase == "all" {
if flag == "time" { if flag == "time" {
alltime = val != 0 alltime = val != 0
} else if flag == "mem" {
allmem = val != 0
} else if flag == "dump" {
alldump = val != 0
if alldump {
BuildDump = valString
}
} else { } else {
return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
} }
@ -186,6 +264,8 @@ func PhaseOption(phase, flag string, val int) string {
BuildTest = val BuildTest = val
case "stats": case "stats":
BuildStats = val BuildStats = val
case "dump":
BuildDump = valString
default: default:
return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
} }
@ -205,6 +285,10 @@ func PhaseOption(phase, flag string, val int) string {
for i, p := range passes { for i, p := range passes {
if phase == "all" { if phase == "all" {
p.time = alltime p.time = alltime
p.mem = allmem
if alldump {
p.addDump(valString)
}
passes[i] = p passes[i] = p
matchedOne = true matchedOne = true
} else if p.name == phase || p.name == underphase || re != nil && re.MatchString(p.name) { } else if p.name == phase || p.name == underphase || re != nil && re.MatchString(p.name) {
@ -223,6 +307,8 @@ func PhaseOption(phase, flag string, val int) string {
p.stats = val p.stats = val
case "test": case "test":
p.test = val p.test = val
case "dump":
p.addDump(valString)
default: default:
return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
} }

View File

@ -7,6 +7,7 @@ package ssa
import ( import (
"fmt" "fmt"
"math" "math"
"strings"
) )
// A Func represents a Go func declaration (or function literal) and // A Func represents a Go func declaration (or function literal) and
@ -113,7 +114,7 @@ func (f *Func) LogStat(key string, args ...interface{}) {
} }
n := "missing_pass" n := "missing_pass"
if f.pass != nil { if f.pass != nil {
n = f.pass.name n = strings.Replace(f.pass.name, " ", "_", -1)
} }
f.Config.Warnl(f.Entry.Line, "\t%s\t%s%s\t%s", n, key, value, f.Name) f.Config.Warnl(f.Entry.Line, "\t%s\t%s%s\t%s", n, key, value, f.Name)
} }