go/src/cmd/compile/internal/gc/flag.go

517 lines
16 KiB
Go
Raw Normal View History

[dev.regabi] cmd/compile: clean up flag handling [generated] The flag values have grown fairly haphazard, with no organization or even common naming convention. This CL moves all flag values into the Flag struct (formerly misnamed Debug), except for a few that live in Ctxt fields instead. This CL is entirely automated changes. A followup CL will make a few manual cleanups, leaving this CL completely automated and easier to regenerate during merge conflicts. Cleaning up flags is necessary because the printing routines look at some of them, and the printing routines need to move out of package gc to a new package shared by gc and any other packages that split out of gc. [git-generate] cd src/cmd/compile/internal/gc rf ' mv Debug Flag mv DebugFlags Flags mv Flags.e Flags.LowerE mv Flags.h Flags.LowerH mv Flags.j Flags.LowerJ mv Flags.l Flags.LowerL mv Flags.m Flags.LowerM mv Flags.r Flags.LowerR mv Flags.w Flags.LowerW mv Flags.P Flags.Percent mv compiling_runtime Flag.CompilingRuntime mv compiling_std Flag.Std mv localimport Flag.D mv asmhdr Flag.AsmHdr mv buildid Flag.BuildID mv nBackendWorkers Flag.LowerC mv pure_go Flag.Complete mv debugstr Flag.LowerD mv flagDWARF Flag.Dwarf mv genDwarfInline Flag.GenDwarfInl mv flag_installsuffix Flag.InstallSuffix mv flag_lang Flag.Lang mv linkobj Flag.LinkObj mv debuglive Flag.Live mv flag_msan Flag.MSan mv nolocalimports Flag.NoLocalImports mv outfile Flag.LowerO mv myimportpath Ctxt.Pkgpath mv writearchive Flag.Pack mv flag_race Flag.Race mv spectre Flag.Spectre mv trace Flag.LowerT mv pathPrefix Flag.TrimPath mv Debug_vlog Ctxt.Debugvlog mv use_writebarrier Flag.WB mv Main.flag_shared Flag.Shared mv Main.flag_dynlink Flag.Dynlink mv Main.goversion Flag.GoVersion mv Main.symabisPath Flag.SymABIs mv cpuprofile Flag.CPUProfile mv memprofile Flag.MemProfile mv traceprofile Flag.TraceProfile mv blockprofile Flag.BlockProfile mv mutexprofile Flag.MutexProfile mv benchfile Flag.Bench mv Main.smallFrames Flag.SmallFrames mv Main.jsonLogOpt Flag.JSON add Flag:$ \ Cfg struct{} mv embedCfg Flag.Cfg.Embed mv idirs Flag.Cfg.ImportDirs mv importMap Flag.Cfg.ImportMap mv packageFile Flag.Cfg.PackageFile mv spectreIndex Flag.Cfg.SpectreIndex mv addidir addImportDir mv main.go:/Wasm/-0,/ssaDump/-3 ParseFlags mv usage Flag Flags ParseFlags \ concurrentFlagOk concurrentBackendAllowed \ addImportDir addImportMap \ readImportCfg readEmbedCfg \ flag.go # Remove //go:generate line copied from main.go # along with two self-assignments from the merge. rm flag.go:/go:generate/-+ \ flag.go:/Ctxt.Pkgpath = Ctxt.Pkgpath/-+ \ flag.go:/Ctxt.Debugvlog = Ctxt.Debugvlog/-+ ' Change-Id: I10431c15fe7d9f48024d53141d4224d957dbf334 Reviewed-on: https://go-review.googlesource.com/c/go/+/271667 Trust: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-16 00:59:30 -05:00
// Copyright 2009 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 gc
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"runtime"
"strconv"
"strings"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
"cmd/compile/internal/types"
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
)
func usage() {
fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
objabi.Flagprint(os.Stderr)
Exit(2)
}
var Flag Flags
// gc debug flags
type Flags struct {
Percent, B, C, E,
K, L, N, S,
W, LowerE, LowerH, LowerJ,
LowerL, LowerM, LowerR, LowerW int
CompilingRuntime bool
Std bool
D string
AsmHdr string
BuildID string
LowerC int
Complete bool
LowerD string
Dwarf bool
GenDwarfInl int
InstallSuffix string
Lang string
LinkObj string
Live int
MSan bool
NoLocalImports bool
LowerO string
Pack bool
Race bool
Spectre string
LowerT bool
TrimPath string
WB bool
Shared bool
Dynlink bool
GoVersion string
SymABIs string
CPUProfile string
MemProfile string
TraceProfile string
BlockProfile string
MutexProfile string
Bench string
SmallFrames bool
JSON string
Cfg struct {
Embed struct {
Patterns map[string][]string
Files map[string]string
}
ImportDirs []string
ImportMap map[string]string
PackageFile map[string]string
SpectreIndex bool
}
}
func ParseFlags() {
Wasm := objabi.GOARCH == "wasm"
// Whether the limit for stack-allocated objects is much smaller than normal.
// This can be helpful for diagnosing certain causes of GC latency. See #27732.
Flag.SmallFrames = false
Flag.JSON = ""
flag.BoolVar(&Flag.CompilingRuntime, "+", false, "compiling runtime")
flag.BoolVar(&Flag.Std, "std", false, "compiling standard library")
flag.StringVar(&Flag.D, "D", "", "set relative `path` for local imports")
objabi.Flagcount("%", "debug non-static initializers", &Flag.Percent)
objabi.Flagcount("B", "disable bounds checking", &Flag.B)
objabi.Flagcount("C", "disable printing of columns in error messages", &Flag.C)
objabi.Flagcount("E", "debug symbol export", &Flag.E)
objabi.Flagcount("K", "debug missing line numbers", &Flag.K)
objabi.Flagcount("L", "show full file names in error messages", &Flag.L)
objabi.Flagcount("N", "disable optimizations", &Flag.N)
objabi.Flagcount("S", "print assembly listing", &Flag.S)
objabi.Flagcount("W", "debug parse tree after type checking", &Flag.W)
objabi.Flagcount("e", "no limit on number of errors reported", &Flag.LowerE)
objabi.Flagcount("h", "halt on error", &Flag.LowerH)
objabi.Flagcount("j", "debug runtime-initialized variables", &Flag.LowerJ)
objabi.Flagcount("l", "disable inlining", &Flag.LowerL)
objabi.Flagcount("m", "print optimization decisions", &Flag.LowerM)
objabi.Flagcount("r", "debug generated wrappers", &Flag.LowerR)
objabi.Flagcount("w", "debug type checking", &Flag.LowerW)
objabi.Flagfn1("I", "add `directory` to import search path", addImportDir)
objabi.AddVersionFlag() // -V
flag.StringVar(&Flag.AsmHdr, "asmhdr", "", "write assembly header to `file`")
flag.StringVar(&Flag.BuildID, "buildid", "", "record `id` as the build id in the export metadata")
flag.IntVar(&Flag.LowerC, "c", 1, "concurrency during compilation, 1 means no concurrency")
flag.BoolVar(&Flag.Complete, "complete", false, "compiling complete package (no C or assembly)")
flag.StringVar(&Flag.LowerD, "d", "", "print debug information about items in `list`; try -d help")
flag.BoolVar(&Flag.Dwarf, "dwarf", !Wasm, "generate DWARF symbols")
flag.BoolVar(&Ctxt.Flag_locationlists, "dwarflocationlists", true, "add location lists to DWARF in optimized mode")
flag.IntVar(&Flag.GenDwarfInl, "gendwarfinl", 2, "generate DWARF inline info records")
objabi.Flagfn1("embedcfg", "read go:embed configuration from `file`", readEmbedCfg)
objabi.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap)
objabi.Flagfn1("importcfg", "read import configuration from `file`", readImportCfg)
flag.StringVar(&Flag.InstallSuffix, "installsuffix", "", "set pkg directory `suffix`")
flag.StringVar(&Flag.Lang, "lang", "", "release to compile for")
flag.StringVar(&Flag.LinkObj, "linkobj", "", "write linker-specific object to `file`")
objabi.Flagcount("live", "debug liveness analysis", &Flag.Live)
if sys.MSanSupported(objabi.GOOS, objabi.GOARCH) {
flag.BoolVar(&Flag.MSan, "msan", false, "build code compatible with C/C++ memory sanitizer")
}
flag.BoolVar(&Flag.NoLocalImports, "nolocalimports", false, "reject local (relative) imports")
flag.StringVar(&Flag.LowerO, "o", "", "write output to `file`")
flag.StringVar(&Ctxt.Pkgpath, "p", "", "set expected package import `path`")
flag.BoolVar(&Flag.Pack, "pack", false, "write to file.a instead of file.o")
if sys.RaceDetectorSupported(objabi.GOOS, objabi.GOARCH) {
flag.BoolVar(&Flag.Race, "race", false, "enable race detector")
}
flag.StringVar(&Flag.Spectre, "spectre", Flag.Spectre, "enable spectre mitigations in `list` (all, index, ret)")
if enableTrace {
flag.BoolVar(&Flag.LowerT, "t", false, "trace type-checking")
}
flag.StringVar(&Flag.TrimPath, "trimpath", "", "remove `prefix` from recorded source file paths")
flag.BoolVar(&Ctxt.Debugvlog, "v", false, "increase debug verbosity")
flag.BoolVar(&Flag.WB, "wb", true, "enable write barrier")
if supportsDynlink(thearch.LinkArch.Arch) {
flag.BoolVar(&Flag.Shared, "shared", false, "generate code that can be linked into a shared library")
flag.BoolVar(&Flag.Dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries")
flag.BoolVar(&Ctxt.Flag_linkshared, "linkshared", false, "generate code that will be linked against Go shared libraries")
}
flag.StringVar(&Flag.CPUProfile, "cpuprofile", "", "write cpu profile to `file`")
flag.StringVar(&Flag.MemProfile, "memprofile", "", "write memory profile to `file`")
flag.Int64Var(&memprofilerate, "memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
flag.StringVar(&Flag.GoVersion, "goversion", "", "required version of the runtime")
flag.StringVar(&Flag.SymABIs, "symabis", "", "read symbol ABIs from `file`")
flag.StringVar(&Flag.TraceProfile, "traceprofile", "", "write an execution trace to `file`")
flag.StringVar(&Flag.BlockProfile, "blockprofile", "", "write block profile to `file`")
flag.StringVar(&Flag.MutexProfile, "mutexprofile", "", "write mutex profile to `file`")
flag.StringVar(&Flag.Bench, "bench", "", "append benchmark times to `file`")
flag.BoolVar(&Flag.SmallFrames, "smallframes", false, "reduce the size limit for stack allocated objects")
flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF")
flag.StringVar(&Flag.JSON, "json", "", "version,destination for JSON compiler/optimizer logging")
objabi.Flagparse(usage)
for _, f := range strings.Split(Flag.Spectre, ",") {
f = strings.TrimSpace(f)
switch f {
default:
log.Fatalf("unknown setting -spectre=%s", f)
case "":
// nothing
case "all":
Flag.Cfg.SpectreIndex = true
Ctxt.Retpoline = true
case "index":
Flag.Cfg.SpectreIndex = true
case "ret":
Ctxt.Retpoline = true
}
}
if Flag.Cfg.SpectreIndex {
switch objabi.GOARCH {
case "amd64":
// ok
default:
log.Fatalf("GOARCH=%s does not support -spectre=index", objabi.GOARCH)
}
}
// Record flags that affect the build result. (And don't
// record flags that don't, since that would cause spurious
// changes in the binary.)
recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
if Flag.SmallFrames {
maxStackVarSize = 128 * 1024
maxImplicitStackVarSize = 16 * 1024
}
Ctxt.Flag_shared = Flag.Dynlink || Flag.Shared
Ctxt.Flag_dynlink = Flag.Dynlink
Ctxt.Flag_optimize = Flag.N == 0
Ctxt.Debugasm = Flag.S
if Flag.Dwarf {
Ctxt.DebugInfo = debuginfo
Ctxt.GenAbstractFunc = genAbstractFunc
Ctxt.DwFixups = obj.NewDwarfFixupTable(Ctxt)
} else {
// turn off inline generation if no dwarf at all
Flag.GenDwarfInl = 0
Ctxt.Flag_locationlists = false
}
if flag.NArg() < 1 && Flag.LowerD != "help" && Flag.LowerD != "ssa/help" {
usage()
}
if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
Exit(2)
}
checkLang()
if Flag.SymABIs != "" {
readSymABIs(Flag.SymABIs, Ctxt.Pkgpath)
}
thearch.LinkArch.Init(Ctxt)
if Flag.LowerO == "" {
p := flag.Arg(0)
if i := strings.LastIndex(p, "/"); i >= 0 {
p = p[i+1:]
}
if runtime.GOOS == "windows" {
if i := strings.LastIndex(p, `\`); i >= 0 {
p = p[i+1:]
}
}
if i := strings.LastIndex(p, "."); i >= 0 {
p = p[:i]
}
suffix := ".o"
if Flag.Pack {
suffix = ".a"
}
Flag.LowerO = p + suffix
}
startProfile()
if Flag.Race && Flag.MSan {
log.Fatal("cannot use both -race and -msan")
}
if Flag.Race || Flag.MSan {
// -race and -msan imply -d=checkptr for now.
Debug_checkptr = 1
}
if ispkgin(omit_pkgs) {
Flag.Race = false
Flag.MSan = false
}
if Flag.Race {
racepkg = types.NewPkg("runtime/race", "")
}
if Flag.MSan {
msanpkg = types.NewPkg("runtime/msan", "")
}
if Flag.Race || Flag.MSan {
instrumenting = true
}
if Flag.CompilingRuntime && Flag.N != 0 {
log.Fatal("cannot disable optimizations while compiling runtime")
}
if Flag.LowerC < 1 {
log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
}
if Flag.LowerC > 1 && !concurrentBackendAllowed() {
log.Fatalf("cannot use concurrent backend compilation with provided flags; invoked as %v", os.Args)
}
if Ctxt.Flag_locationlists && len(Ctxt.Arch.DWARFRegisters) == 0 {
log.Fatalf("location lists requested but register mapping not available on %v", Ctxt.Arch.Name)
}
// parse -d argument
if Flag.LowerD != "" {
Split:
for _, name := range strings.Split(Flag.LowerD, ",") {
if name == "" {
continue
}
// display help about the -d option itself and quit
if name == "help" {
fmt.Print(debugHelpHeader)
maxLen := len("ssa/help")
for _, t := range debugtab {
if len(t.name) > maxLen {
maxLen = len(t.name)
}
}
for _, t := range debugtab {
fmt.Printf("\t%-*s\t%s\n", maxLen, t.name, t.help)
}
// ssa options have their own help
fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging")
fmt.Print(debugHelpFooter)
os.Exit(0)
}
val, valstring, haveInt := 1, "", true
if i := strings.IndexAny(name, "=:"); i >= 0 {
var err error
name, valstring = name[:i], name[i+1:]
val, err = strconv.Atoi(valstring)
if err != nil {
val, haveInt = 1, false
}
}
for _, t := range debugtab {
if t.name != name {
continue
}
switch vp := t.val.(type) {
case nil:
// Ignore
case *string:
*vp = valstring
case *int:
if !haveInt {
log.Fatalf("invalid debug value %v", name)
}
*vp = val
default:
panic("bad debugtab type")
}
continue Split
}
// special case for ssa for now
if strings.HasPrefix(name, "ssa/") {
// expect form ssa/phase/flag
// e.g. -d=ssa/generic_cse/time
// _ in phase name also matches space
phase := name[4:]
flag := "debug" // default flag is debug
if i := strings.Index(phase, "/"); i >= 0 {
flag = phase[i+1:]
phase = phase[:i]
}
err := ssa.PhaseOption(phase, flag, val, valstring)
if err != "" {
log.Fatalf(err)
}
continue Split
}
log.Fatalf("unknown debug key -d %s\n", name)
}
}
if Flag.CompilingRuntime {
// Runtime can't use -d=checkptr, at least not yet.
Debug_checkptr = 0
// Fuzzing the runtime isn't interesting either.
Debug_libfuzzer = 0
}
// set via a -d flag
Ctxt.Debugpcln = Debug_pctab
if Flag.Dwarf {
dwarf.EnableLogging(Debug_gendwarfinl != 0)
}
if Debug_softfloat != 0 {
thearch.SoftFloat = true
}
// enable inlining. for now:
// default: inlining on. (Debug.l == 1)
// -l: inlining off (Debug.l == 0)
// -l=2, -l=3: inlining on again, with extra debugging (Debug.l > 1)
if Flag.LowerL <= 1 {
Flag.LowerL = 1 - Flag.LowerL
}
if Flag.JSON != "" { // parse version,destination from json logging optimization.
logopt.LogJsonOption(Flag.JSON)
}
}
// concurrentFlagOk reports whether the current compiler flags
// are compatible with concurrent compilation.
func concurrentFlagOk() bool {
// TODO(rsc): Many of these are fine. Remove them.
return Flag.Percent == 0 &&
Flag.E == 0 &&
Flag.K == 0 &&
Flag.L == 0 &&
Flag.LowerH == 0 &&
Flag.LowerJ == 0 &&
Flag.LowerM == 0 &&
Flag.LowerR == 0
}
func concurrentBackendAllowed() bool {
if !concurrentFlagOk() {
return false
}
// Debug.S by itself is ok, because all printing occurs
// while writing the object file, and that is non-concurrent.
// Adding Debug_vlog, however, causes Debug.S to also print
// while flushing the plist, which happens concurrently.
if Ctxt.Debugvlog || Flag.LowerD != "" || Flag.Live > 0 {
return false
}
// TODO: Test and delete this condition.
if objabi.Fieldtrack_enabled != 0 {
return false
}
// TODO: fix races and enable the following flags
if Ctxt.Flag_shared || Ctxt.Flag_dynlink || Flag.Race {
return false
}
return true
}
func addImportDir(dir string) {
if dir != "" {
Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
}
}
func addImportMap(s string) {
if Flag.Cfg.ImportMap == nil {
Flag.Cfg.ImportMap = make(map[string]string)
}
if strings.Count(s, "=") != 1 {
log.Fatal("-importmap argument must be of the form source=actual")
}
i := strings.Index(s, "=")
source, actual := s[:i], s[i+1:]
if source == "" || actual == "" {
log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty")
}
Flag.Cfg.ImportMap[source] = actual
}
func readImportCfg(file string) {
if Flag.Cfg.ImportMap == nil {
Flag.Cfg.ImportMap = make(map[string]string)
}
Flag.Cfg.PackageFile = map[string]string{}
data, err := ioutil.ReadFile(file)
if err != nil {
log.Fatalf("-importcfg: %v", err)
}
for lineNum, line := range strings.Split(string(data), "\n") {
lineNum++ // 1-based
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
var verb, args string
if i := strings.Index(line, " "); i < 0 {
verb = line
} else {
verb, args = line[:i], strings.TrimSpace(line[i+1:])
}
var before, after string
if i := strings.Index(args, "="); i >= 0 {
before, after = args[:i], args[i+1:]
}
switch verb {
default:
log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
case "importmap":
if before == "" || after == "" {
log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
}
Flag.Cfg.ImportMap[before] = after
case "packagefile":
if before == "" || after == "" {
log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
}
Flag.Cfg.PackageFile[before] = after
}
}
}
func readEmbedCfg(file string) {
data, err := ioutil.ReadFile(file)
if err != nil {
log.Fatalf("-embedcfg: %v", err)
}
if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
log.Fatalf("%s: %v", file, err)
}
if Flag.Cfg.Embed.Patterns == nil {
log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
}
if Flag.Cfg.Embed.Files == nil {
log.Fatalf("%s: invalid embedcfg: missing Files", file)
}
}