diff options
Diffstat (limited to 'src/cmd/compile/internal/gc/main.go')
-rw-r--r-- | src/cmd/compile/internal/gc/main.go | 862 |
1 files changed, 189 insertions, 673 deletions
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 7015d9d6cd..6b7123dc71 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -9,6 +9,8 @@ package gc import ( "bufio" "bytes" + "cmd/compile/internal/base" + "cmd/compile/internal/ir" "cmd/compile/internal/logopt" "cmd/compile/internal/ssa" "cmd/compile/internal/types" @@ -18,9 +20,9 @@ import ( "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/src" - "cmd/internal/sys" "flag" "fmt" + "go/constant" "internal/goversion" "io" "io/ioutil" @@ -34,111 +36,20 @@ import ( "strings" ) -var ( - buildid string - spectre string - spectreIndex bool -) - -var ( - Debug_append int - Debug_checkptr int - Debug_closure int - Debug_compilelater int - debug_dclstack int - Debug_dumpptrs int - Debug_libfuzzer int - Debug_panic int - Debug_slice int - Debug_vlog bool - Debug_wb int - Debug_pctab string - Debug_locationlist int - Debug_typecheckinl int - Debug_gendwarfinl int - Debug_softfloat int - Debug_defer int -) - -// Debug arguments. -// These can be specified with the -d flag, as in "-d nil" -// to set the debug_checknil variable. -// Multiple options can be comma-separated. -// Each option accepts an optional argument, as in "gcprog=2" -var debugtab = []struct { - name string - help string - val interface{} // must be *int or *string -}{ - {"append", "print information about append compilation", &Debug_append}, - {"checkptr", "instrument unsafe pointer conversions", &Debug_checkptr}, - {"closure", "print information about closure compilation", &Debug_closure}, - {"compilelater", "compile functions as late as possible", &Debug_compilelater}, - {"disablenil", "disable nil checks", &disable_checknil}, - {"dclstack", "run internal dclstack check", &debug_dclstack}, - {"dumpptrs", "show Node pointer values in Dump/dumplist output", &Debug_dumpptrs}, - {"gcprog", "print dump of GC programs", &Debug_gcprog}, - {"libfuzzer", "coverage instrumentation for libfuzzer", &Debug_libfuzzer}, - {"nil", "print information about nil checks", &Debug_checknil}, - {"panic", "do not hide any compiler panic", &Debug_panic}, - {"slice", "print information about slice compilation", &Debug_slice}, - {"typeassert", "print information about type assertion inlining", &Debug_typeassert}, - {"wb", "print information about write barriers", &Debug_wb}, - {"export", "print export data", &Debug_export}, - {"pctab", "print named pc-value table", &Debug_pctab}, - {"locationlists", "print information about DWARF location list creation", &Debug_locationlist}, - {"typecheckinl", "eager typechecking of inline function bodies", &Debug_typecheckinl}, - {"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl}, - {"softfloat", "force compiler to emit soft-float code", &Debug_softfloat}, - {"defer", "print information about defer compilation", &Debug_defer}, - {"fieldtrack", "enable fieldtracking", &objabi.Fieldtrack_enabled}, -} - -const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>] - -<key> is one of: - -` - -const debugHelpFooter = ` -<value> is key-specific. - -Key "checkptr" supports values: - "0": instrumentation disabled - "1": conversions involving unsafe.Pointer are instrumented - "2": conversions to unsafe.Pointer force heap allocation - -Key "pctab" supports values: - "pctospadj", "pctofile", "pctoline", "pctoinline", "pctopcdata" -` - -func usage() { - fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n") - objabi.Flagprint(os.Stderr) - Exit(2) -} - func hidePanic() { - if Debug_panic == 0 && nsavederrors+nerrors > 0 { + if base.Debug.Panic == 0 && base.Errors() > 0 { // If we've already complained about things // in the program, don't bother complaining // about a panic too; let the user clean up // the code and try again. if err := recover(); err != nil { - errorexit() + base.ErrorExit() } } } -// supportsDynlink reports whether or not the code generator for the given -// architecture supports the -shared and -dynlink flags. -func supportsDynlink(arch *sys.Arch) bool { - return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) -} - // timing data for compiler phases var timings Timings -var benchfile string var nowritebarrierrecCheck *nowritebarrierrecChecker @@ -152,28 +63,28 @@ func Main(archInit func(*Arch)) { archInit(&thearch) - Ctxt = obj.Linknew(thearch.LinkArch) - Ctxt.DiagFunc = yyerror - Ctxt.DiagFlush = flusherrors - Ctxt.Bso = bufio.NewWriter(os.Stdout) + base.Ctxt = obj.Linknew(thearch.LinkArch) + base.Ctxt.DiagFunc = base.Errorf + base.Ctxt.DiagFlush = base.FlushErrors + base.Ctxt.Bso = bufio.NewWriter(os.Stdout) // UseBASEntries is preferred because it shaves about 2% off build time, but LLDB, dsymutil, and dwarfdump // on Darwin don't support it properly, especially since macOS 10.14 (Mojave). This is exposed as a flag // to allow testing with LLVM tools on Linux, and to help with reporting this bug to the LLVM project. // See bugs 31188 and 21945 (CLs 170638, 98075, 72371). - Ctxt.UseBASEntries = Ctxt.Headtype != objabi.Hdarwin + base.Ctxt.UseBASEntries = base.Ctxt.Headtype != objabi.Hdarwin - localpkg = types.NewPkg("", "") - localpkg.Prefix = "\"\"" + ir.LocalPkg = types.NewPkg("", "") + ir.LocalPkg.Prefix = "\"\"" // We won't know localpkg's height until after import // processing. In the mean time, set to MaxPkgHeight to ensure // height comparisons at least work until then. - localpkg.Height = types.MaxPkgHeight + ir.LocalPkg.Height = types.MaxPkgHeight // pseudo-package, for scoping - builtinpkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin? - builtinpkg.Prefix = "go.builtin" // not go%2ebuiltin + ir.BuiltinPkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin? + ir.BuiltinPkg.Prefix = "go.builtin" // not go%2ebuiltin // pseudo-package, accessed by import "unsafe" unsafepkg = types.NewPkg("unsafe", "unsafe") @@ -203,321 +114,75 @@ func Main(archInit func(*Arch)) { // pseudo-package used for methods with anonymous receivers gopkg = types.NewPkg("go", "") - 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. - smallFrames := false - jsonLogOpt := "" - - flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime") - flag.BoolVar(&compiling_std, "std", false, "compiling standard library") - flag.StringVar(&localimport, "D", "", "set relative `path` for local imports") - - objabi.Flagcount("%", "debug non-static initializers", &Debug.P) - objabi.Flagcount("B", "disable bounds checking", &Debug.B) - objabi.Flagcount("C", "disable printing of columns in error messages", &Debug.C) - objabi.Flagcount("E", "debug symbol export", &Debug.E) - objabi.Flagcount("G", "accept generic code", &Debug.G) - objabi.Flagcount("K", "debug missing line numbers", &Debug.K) - objabi.Flagcount("L", "show full file names in error messages", &Debug.L) - objabi.Flagcount("N", "disable optimizations", &Debug.N) - objabi.Flagcount("S", "print assembly listing", &Debug.S) - objabi.Flagcount("W", "debug parse tree after type checking", &Debug.W) - objabi.Flagcount("e", "no limit on number of errors reported", &Debug.e) - objabi.Flagcount("h", "halt on error", &Debug.h) - objabi.Flagcount("j", "debug runtime-initialized variables", &Debug.j) - objabi.Flagcount("l", "disable inlining", &Debug.l) - objabi.Flagcount("m", "print optimization decisions", &Debug.m) - objabi.Flagcount("r", "debug generated wrappers", &Debug.r) - objabi.Flagcount("w", "debug type checking", &Debug.w) - - objabi.Flagfn1("I", "add `directory` to import search path", addidir) - objabi.AddVersionFlag() // -V - flag.StringVar(&asmhdr, "asmhdr", "", "write assembly header to `file`") - flag.StringVar(&buildid, "buildid", "", "record `id` as the build id in the export metadata") - flag.IntVar(&nBackendWorkers, "c", 1, "concurrency during compilation, 1 means no concurrency") - flag.BoolVar(&pure_go, "complete", false, "compiling complete package (no C or assembly)") - flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`; try -d help") - flag.BoolVar(&flagDWARF, "dwarf", !Wasm, "generate DWARF symbols") - flag.BoolVar(&Ctxt.Flag_locationlists, "dwarflocationlists", true, "add location lists to DWARF in optimized mode") - flag.IntVar(&genDwarfInline, "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(&linkobj, "linkobj", "", "write linker-specific object to `file`") - objabi.Flagcount("live", "debug liveness analysis", &debuglive) - if sys.MSanSupported(objabi.GOOS, objabi.GOARCH) { - flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer") - } - flag.BoolVar(&nolocalimports, "nolocalimports", false, "reject local (relative) imports") - flag.StringVar(&outfile, "o", "", "write output to `file`") - flag.StringVar(&myimportpath, "p", "", "set expected package import `path`") - flag.BoolVar(&writearchive, "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(&spectre, "spectre", spectre, "enable spectre mitigations in `list` (all, index, ret)") - if enableTrace { - flag.BoolVar(&trace, "t", false, "trace type-checking") - } - flag.StringVar(&pathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths") - flag.BoolVar(&Debug_vlog, "v", false, "increase debug verbosity") - flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier") - var flag_shared bool - var flag_dynlink bool - 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(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`") - flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`") - flag.Int64Var(&memprofilerate, "memprofilerate", 0, "set runtime.MemProfileRate to `rate`") - var goversion string - flag.StringVar(&goversion, "goversion", "", "required version of the runtime") - var symabisPath string - flag.StringVar(&symabisPath, "symabis", "", "read symbol ABIs from `file`") - flag.StringVar(&traceprofile, "traceprofile", "", "write an execution trace to `file`") - flag.StringVar(&blockprofile, "blockprofile", "", "write block profile to `file`") - flag.StringVar(&mutexprofile, "mutexprofile", "", "write mutex profile to `file`") - flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`") - flag.BoolVar(&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(&jsonLogOpt, "json", "", "version,destination for JSON compiler/optimizer logging") - - objabi.Flagparse(usage) - - Ctxt.Pkgpath = myimportpath - - for _, f := range strings.Split(spectre, ",") { - f = strings.TrimSpace(f) - switch f { - default: - log.Fatalf("unknown setting -spectre=%s", f) - case "": - // nothing - case "all": - spectreIndex = true - Ctxt.Retpoline = true - case "index": - spectreIndex = true - case "ret": - Ctxt.Retpoline = true - } - } - - if spectreIndex { - switch objabi.GOARCH { - case "amd64": - // ok - default: - log.Fatalf("GOARCH=%s does not support -spectre=index", objabi.GOARCH) - } - } + base.DebugSSA = ssa.PhaseOption + base.ParseFlags() // 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 smallFrames { + if !enableTrace && base.Flag.LowerT { + log.Fatalf("compiler not built with support for -t") + } + + // Enable inlining (after recordFlags, to avoid recording the rewritten -l). For now: + // default: inlining on. (Flag.LowerL == 1) + // -l: inlining off (Flag.LowerL == 0) + // -l=2, -l=3: inlining on again, with extra debugging (Flag.LowerL > 1) + if base.Flag.LowerL <= 1 { + base.Flag.LowerL = 1 - base.Flag.LowerL + } + + if base.Flag.SmallFrames { maxStackVarSize = 128 * 1024 maxImplicitStackVarSize = 16 * 1024 } - Ctxt.Flag_shared = flag_dynlink || flag_shared - Ctxt.Flag_dynlink = flag_dynlink - Ctxt.Flag_optimize = Debug.N == 0 - - Ctxt.Debugasm = Debug.S - Ctxt.Debugvlog = Debug_vlog - if flagDWARF { - Ctxt.DebugInfo = debuginfo - Ctxt.GenAbstractFunc = genAbstractFunc - Ctxt.DwFixups = obj.NewDwarfFixupTable(Ctxt) + if base.Flag.Dwarf { + base.Ctxt.DebugInfo = debuginfo + base.Ctxt.GenAbstractFunc = genAbstractFunc + base.Ctxt.DwFixups = obj.NewDwarfFixupTable(base.Ctxt) } else { // turn off inline generation if no dwarf at all - genDwarfInline = 0 - Ctxt.Flag_locationlists = false - } - - if flag.NArg() < 1 && debugstr != "help" && debugstr != "ssa/help" { - usage() + base.Flag.GenDwarfInl = 0 + base.Ctxt.Flag_locationlists = false } - - if goversion != "" && goversion != runtime.Version() { - fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), goversion) - Exit(2) + if base.Ctxt.Flag_locationlists && len(base.Ctxt.Arch.DWARFRegisters) == 0 { + log.Fatalf("location lists requested but register mapping not available on %v", base.Ctxt.Arch.Name) } checkLang() - if symabisPath != "" { - readSymABIs(symabisPath, myimportpath) + if base.Flag.SymABIs != "" { + readSymABIs(base.Flag.SymABIs, base.Ctxt.Pkgpath) } - thearch.LinkArch.Init(Ctxt) - - if outfile == "" { - 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 writearchive { - suffix = ".a" - } - outfile = p + suffix + if ispkgin(omit_pkgs) { + base.Flag.Race = false + base.Flag.MSan = false } + thearch.LinkArch.Init(base.Ctxt) 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 { + if base.Flag.Race { racepkg = types.NewPkg("runtime/race", "") } - if flag_msan { + if base.Flag.MSan { msanpkg = types.NewPkg("runtime/msan", "") } - if flag_race || flag_msan { + if base.Flag.Race || base.Flag.MSan { instrumenting = true } - - if compiling_runtime && Debug.N != 0 { - log.Fatal("cannot disable optimizations while compiling runtime") - } - if nBackendWorkers < 1 { - log.Fatalf("-c must be at least 1, got %d", nBackendWorkers) - } - if nBackendWorkers > 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) + if base.Flag.Dwarf { + dwarf.EnableLogging(base.Debug.DwarfInl != 0) } - - // parse -d argument - if debugstr != "" { - Split: - for _, name := range strings.Split(debugstr, ",") { - 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 compiling_runtime { - // 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 flagDWARF { - dwarf.EnableLogging(Debug_gendwarfinl != 0) - } - - if Debug_softfloat != 0 { + if base.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 Debug.l <= 1 { - Debug.l = 1 - Debug.l - } - - if jsonLogOpt != "" { // parse version,destination from json logging optimization. - logopt.LogJsonOption(jsonLogOpt) + if base.Flag.JSON != "" { // parse version,destination from json logging optimization. + logopt.LogJsonOption(base.Flag.JSON) } ssaDump = os.Getenv("GOSSAFUNC") @@ -534,7 +199,7 @@ func Main(archInit func(*Arch)) { } } - trackScopes = flagDWARF + trackScopes = base.Flag.Dwarf Widthptr = thearch.LinkArch.PtrSize Widthreg = thearch.LinkArch.RegSize @@ -544,31 +209,19 @@ func Main(archInit func(*Arch)) { // would lead to import cycles) types.Widthptr = Widthptr types.Dowidth = dowidth - types.Fatalf = Fatalf - types.Sconv = func(s *types.Sym, flag, mode int) string { - return sconv(s, FmtFlag(flag), fmtMode(mode)) - } - types.Tconv = func(t *types.Type, flag, mode int) string { - return tconv(t, FmtFlag(flag), fmtMode(mode)) - } - types.FormatSym = func(sym *types.Sym, s fmt.State, verb rune, mode int) { - symFormat(sym, s, verb, fmtMode(mode)) - } - types.FormatType = func(t *types.Type, s fmt.State, verb rune, mode int) { - typeFormat(t, s, verb, fmtMode(mode)) - } + types.Fatalf = base.Fatalf + ir.InstallTypeFormats() types.TypeLinkSym = func(t *types.Type) *obj.LSym { return typenamesym(t).Linksym() } - types.FmtLeft = int(FmtLeft) - types.FmtUnsigned = int(FmtUnsigned) - types.FErr = int(FErr) - types.Ctxt = Ctxt + types.FmtLeft = int(ir.FmtLeft) + types.FmtUnsigned = int(ir.FmtUnsigned) + types.FErr = int(ir.FErr) + types.Ctxt = base.Ctxt initUniverse() - dclcontext = PEXTERN - nerrors = 0 + dclcontext = ir.PEXTERN autogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0) @@ -576,14 +229,12 @@ func Main(archInit func(*Arch)) { loadsys() timings.Start("fe", "parse") - lines := parseFiles(flag.Args(), Debug.G != 0) + lines := parseFiles(flag.Args(), base.Flag.G != 0) timings.Stop() timings.AddEvent(int64(lines), "lines") - if Debug.G != 0 { + if base.Flag.G != 0 { // can only parse generic code for now - if nerrors+nsavederrors != 0 { - errorexit() - } + base.ExitIfErrors() return } @@ -607,7 +258,7 @@ func Main(archInit func(*Arch)) { timings.Start("fe", "typecheck", "top1") for i := 0; i < len(xtop); i++ { n := xtop[i] - if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias()) { + if op := n.Op(); op != ir.ODCL && op != ir.OAS && op != ir.OAS2 && (op != ir.ODCLTYPE || !n.Left().Name().Param.Alias()) { xtop[i] = typecheck(n, ctxStmt) } } @@ -619,7 +270,7 @@ func Main(archInit func(*Arch)) { timings.Start("fe", "typecheck", "top2") for i := 0; i < len(xtop); i++ { n := xtop[i] - if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias() { + if op := n.Op(); op == ir.ODCL || op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.Left().Name().Param.Alias() { xtop[i] = typecheck(n, ctxStmt) } } @@ -630,14 +281,14 @@ func Main(archInit func(*Arch)) { var fcount int64 for i := 0; i < len(xtop); i++ { n := xtop[i] - if n.Op == ODCLFUNC { + if n.Op() == ir.ODCLFUNC { Curfn = n decldepth = 1 - saveerrors() - typecheckslice(Curfn.Nbody.Slice(), ctxStmt) + errorsBefore := base.Errors() + typecheckslice(Curfn.Body().Slice(), ctxStmt) checkreturn(Curfn) - if nerrors != 0 { - Curfn.Nbody.Set(nil) // type errors; do not compile + if base.Errors() > errorsBefore { + Curfn.PtrBody().Set(nil) // type errors; do not compile } // Now that we've checked whether n terminates, // we can eliminate some obviously dead code. @@ -649,11 +300,9 @@ func Main(archInit func(*Arch)) { // check past phase 9 isn't sufficient, as we may exit with other errors // before then, thus skipping map key errors. checkMapKeys() - timings.AddEvent(fcount, "funcs") + base.ExitIfErrors() - if nsavederrors+nerrors != 0 { - errorexit() - } + timings.AddEvent(fcount, "funcs") fninit(xtop) @@ -662,39 +311,31 @@ func Main(archInit func(*Arch)) { // because variables captured by value do not escape. timings.Start("fe", "capturevars") for _, n := range xtop { - if n.Op == ODCLFUNC && n.Func.Closure != nil { + if n.Op() == ir.ODCLFUNC && n.Func().OClosure != nil { Curfn = n capturevars(n) } } capturevarscomplete = true - Curfn = nil - - if nsavederrors+nerrors != 0 { - errorexit() - } + base.ExitIfErrors() // Phase 5: Inlining timings.Start("fe", "inlining") - if Debug_typecheckinl != 0 { + if base.Debug.TypecheckInl != 0 { // Typecheck imported function bodies if Debug.l > 1, // otherwise lazily when used or re-exported. for _, n := range importlist { - if n.Func.Inl != nil { - saveerrors() + if n.Func().Inl != nil { typecheckinl(n) } } - - if nsavederrors+nerrors != 0 { - errorexit() - } + base.ExitIfErrors() } - if Debug.l != 0 { + if base.Flag.LowerL != 0 { // Find functions that can be inlined and clone them before walk expands them. - visitBottomUp(xtop, func(list []*Node, recursive bool) { + visitBottomUp(xtop, func(list []ir.Node, recursive bool) { numfns := numNonClosures(list) for _, n := range list { if !recursive || numfns > 1 { @@ -703,8 +344,8 @@ func Main(archInit func(*Arch)) { // across more than one function. caninl(n) } else { - if Debug.m > 1 { - fmt.Printf("%v: cannot inline %v: recursive\n", n.Line(), n.Func.Nname) + if base.Flag.LowerM > 1 { + fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Func().Nname) } } inlcalls(n) @@ -713,7 +354,7 @@ func Main(archInit func(*Arch)) { } for _, n := range xtop { - if n.Op == ODCLFUNC { + if n.Op() == ir.ODCLFUNC { devirtualize(n) } } @@ -734,7 +375,7 @@ func Main(archInit func(*Arch)) { // checking. This must happen before transformclosure. // We'll do the final check after write barriers are // inserted. - if compiling_runtime { + if base.Flag.CompilingRuntime { nowritebarrierrecCheck = newNowritebarrierrecChecker() } @@ -743,7 +384,7 @@ func Main(archInit func(*Arch)) { // before walk reaches a call of a closure. timings.Start("fe", "xclosures") for _, n := range xtop { - if n.Op == ODCLFUNC && n.Func.Closure != nil { + if n.Op() == ir.ODCLFUNC && n.Func().OClosure != nil { Curfn = n transformclosure(n) } @@ -766,7 +407,7 @@ func Main(archInit func(*Arch)) { fcount = 0 for i := 0; i < len(xtop); i++ { n := xtop[i] - if n.Op == ODCLFUNC { + if n.Op() == ir.ODCLFUNC { funccompile(n) fcount++ } @@ -785,33 +426,30 @@ func Main(archInit func(*Arch)) { // Finalize DWARF inline routine DIEs, then explicitly turn off // DWARF inlining gen so as to avoid problems with generated // method wrappers. - if Ctxt.DwFixups != nil { - Ctxt.DwFixups.Finalize(myimportpath, Debug_gendwarfinl != 0) - Ctxt.DwFixups = nil - genDwarfInline = 0 + if base.Ctxt.DwFixups != nil { + base.Ctxt.DwFixups.Finalize(base.Ctxt.Pkgpath, base.Debug.DwarfInl != 0) + base.Ctxt.DwFixups = nil + base.Flag.GenDwarfInl = 0 } // Phase 9: Check external declarations. timings.Start("be", "externaldcls") for i, n := range externdcl { - if n.Op == ONAME { + if n.Op() == ir.ONAME { externdcl[i] = typecheck(externdcl[i], ctxExpr) } } // Check the map keys again, since we typechecked the external // declarations. checkMapKeys() - - if nerrors+nsavederrors != 0 { - errorexit() - } + base.ExitIfErrors() // Write object data to disk. timings.Start("be", "dumpobj") dumpdata() - Ctxt.NumberSyms() + base.Ctxt.NumberSyms() dumpobj() - if asmhdr != "" { + if base.Flag.AsmHdr != "" { dumpasmhdr() } @@ -821,40 +459,37 @@ func Main(archInit func(*Arch)) { }) for _, large := range largeStackFrames { if large.callee != 0 { - yyerrorl(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20) + base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20) } else { - yyerrorl(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20) + base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20) } } if len(funcStack) != 0 { - Fatalf("funcStack is non-empty: %v", len(funcStack)) + base.Fatalf("funcStack is non-empty: %v", len(funcStack)) } if len(compilequeue) != 0 { - Fatalf("%d uncompiled functions", len(compilequeue)) + base.Fatalf("%d uncompiled functions", len(compilequeue)) } - logopt.FlushLoggedOpts(Ctxt, myimportpath) + logopt.FlushLoggedOpts(base.Ctxt, base.Ctxt.Pkgpath) + base.ExitIfErrors() - if nerrors+nsavederrors != 0 { - errorexit() - } - - flusherrors() + base.FlushErrors() timings.Stop() - if benchfile != "" { - if err := writebench(benchfile); err != nil { + if base.Flag.Bench != "" { + if err := writebench(base.Flag.Bench); err != nil { log.Fatalf("cannot write benchmark data: %v", err) } } } // numNonClosures returns the number of functions in list which are not closures. -func numNonClosures(list []*Node) int { +func numNonClosures(list []ir.Node) int { count := 0 for _, n := range list { - if n.Func.Closure == nil { + if n.Func().OClosure == nil { count++ } } @@ -871,7 +506,7 @@ func writebench(filename string) error { fmt.Fprintln(&buf, "commit:", objabi.Version) fmt.Fprintln(&buf, "goos:", runtime.GOOS) fmt.Fprintln(&buf, "goarch:", runtime.GOARCH) - timings.Write(&buf, "BenchmarkCompile:"+myimportpath+":") + timings.Write(&buf, "BenchmarkCompile:"+base.Ctxt.Pkgpath+":") n, err := f.Write(buf.Bytes()) if err != nil { @@ -884,64 +519,6 @@ func writebench(filename string) error { return f.Close() } -var ( - importMap = map[string]string{} - packageFile map[string]string // nil means not in use -) - -func addImportMap(s 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") - } - importMap[source] = actual -} - -func readImportCfg(file string) { - 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) - } - importMap[before] = after - case "packagefile": - if before == "" || after == "" { - log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum) - } - packageFile[before] = after - } - } -} - // symabiDefs and symabiRefs record the defined and referenced ABIs of // symbols required by non-Go code. These are keyed by link symbol // name, where the local package prefix is always `"".` @@ -1013,11 +590,6 @@ func readSymABIs(file, myimportpath string) { } } -func saveerrors() { - nsavederrors += nerrors - nerrors = 0 -} - func arsize(b *bufio.Reader, name string) int { var buf [ArhdrSize]byte if _, err := io.ReadFull(b, buf[:]); err != nil { @@ -1032,14 +604,6 @@ func arsize(b *bufio.Reader, name string) int { return i } -var idirs []string - -func addidir(dir string) { - if dir != "" { - idirs = append(idirs, dir) - } -} - func isDriveLetter(b byte) bool { return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' } @@ -1054,12 +618,12 @@ func islocalname(name string) bool { func findpkg(name string) (file string, ok bool) { if islocalname(name) { - if nolocalimports { + if base.Flag.NoLocalImports { return "", false } - if packageFile != nil { - file, ok = packageFile[name] + if base.Flag.Cfg.PackageFile != nil { + file, ok = base.Flag.Cfg.PackageFile[name] return file, ok } @@ -1081,16 +645,16 @@ func findpkg(name string) (file string, ok bool) { // don't want to see "encoding/../encoding/base64" // as different from "encoding/base64". if q := path.Clean(name); q != name { - yyerror("non-canonical import path %q (should be %q)", name, q) + base.Errorf("non-canonical import path %q (should be %q)", name, q) return "", false } - if packageFile != nil { - file, ok = packageFile[name] + if base.Flag.Cfg.PackageFile != nil { + file, ok = base.Flag.Cfg.PackageFile[name] return file, ok } - for _, dir := range idirs { + for _, dir := range base.Flag.Cfg.ImportDirs { file = fmt.Sprintf("%s/%s.a", dir, name) if _, err := os.Stat(file); err == nil { return file, true @@ -1104,13 +668,13 @@ func findpkg(name string) (file string, ok bool) { if objabi.GOROOT != "" { suffix := "" suffixsep := "" - if flag_installsuffix != "" { + if base.Flag.InstallSuffix != "" { suffixsep = "_" - suffix = flag_installsuffix - } else if flag_race { + suffix = base.Flag.InstallSuffix + } else if base.Flag.Race { suffixsep = "_" suffix = "race" - } else if flag_msan { + } else if base.Flag.MSan { suffixsep = "_" suffix = "msan" } @@ -1147,7 +711,7 @@ func loadsys() { case varTag: importvar(Runtimepkg, src.NoXPos, sym, typ) default: - Fatalf("unhandled declaration tag %v", d.tag) + base.Fatalf("unhandled declaration tag %v", d.tag) } } @@ -1159,15 +723,15 @@ func loadsys() { // imported so far. var myheight int -func importfile(f *Val) *types.Pkg { - path_, ok := f.U.(string) - if !ok { - yyerror("import path must be a string") +func importfile(f constant.Value) *types.Pkg { + if f.Kind() != constant.String { + base.Errorf("import path must be a string") return nil } + path_ := constant.StringVal(f) if len(path_) == 0 { - yyerror("import path is empty") + base.Errorf("import path is empty") return nil } @@ -1180,16 +744,16 @@ func importfile(f *Val) *types.Pkg { // the main package, just as we reserve the import // path "math" to identify the standard math package. if path_ == "main" { - yyerror("cannot import \"main\"") - errorexit() + base.Errorf("cannot import \"main\"") + base.ErrorExit() } - if myimportpath != "" && path_ == myimportpath { - yyerror("import %q while compiling that package (import cycle)", path_) - errorexit() + if base.Ctxt.Pkgpath != "" && path_ == base.Ctxt.Pkgpath { + base.Errorf("import %q while compiling that package (import cycle)", path_) + base.ErrorExit() } - if mapped, ok := importMap[path_]; ok { + if mapped, ok := base.Flag.Cfg.ImportMap[path_]; ok { path_ = mapped } @@ -1199,13 +763,13 @@ func importfile(f *Val) *types.Pkg { if islocalname(path_) { if path_[0] == '/' { - yyerror("import path cannot be absolute path") + base.Errorf("import path cannot be absolute path") return nil } - prefix := Ctxt.Pathname - if localimport != "" { - prefix = localimport + prefix := base.Ctxt.Pathname + if base.Flag.D != "" { + prefix = base.Flag.D } path_ = path.Join(prefix, path_) @@ -1216,8 +780,8 @@ func importfile(f *Val) *types.Pkg { file, found := findpkg(path_) if !found { - yyerror("can't find import: %q", path_) - errorexit() + base.Errorf("can't find import: %q", path_) + base.ErrorExit() } importpkg := types.NewPkg(path_, "") @@ -1229,57 +793,55 @@ func importfile(f *Val) *types.Pkg { imp, err := bio.Open(file) if err != nil { - yyerror("can't open import: %q: %v", path_, err) - errorexit() + base.Errorf("can't open import: %q: %v", path_, err) + base.ErrorExit() } defer imp.Close() // check object header p, err := imp.ReadString('\n') if err != nil { - yyerror("import %s: reading input: %v", file, err) - errorexit() + base.Errorf("import %s: reading input: %v", file, err) + base.ErrorExit() } if p == "!<arch>\n" { // package archive // package export block should be first sz := arsize(imp.Reader, "__.PKGDEF") if sz <= 0 { - yyerror("import %s: not a package file", file) - errorexit() + base.Errorf("import %s: not a package file", file) + base.ErrorExit() } p, err = imp.ReadString('\n') if err != nil { - yyerror("import %s: reading input: %v", file, err) - errorexit() + base.Errorf("import %s: reading input: %v", file, err) + base.ErrorExit() } } if !strings.HasPrefix(p, "go object ") { - yyerror("import %s: not a go object file: %s", file, p) - errorexit() + base.Errorf("import %s: not a go object file: %s", file, p) + base.ErrorExit() } q := fmt.Sprintf("%s %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version, objabi.Expstring()) if p[10:] != q { - yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q) - errorexit() + base.Errorf("import %s: object is [%s] expected [%s]", file, p[10:], q) + base.ErrorExit() } // process header lines for { p, err = imp.ReadString('\n') if err != nil { - yyerror("import %s: reading input: %v", file, err) - errorexit() + base.Errorf("import %s: reading input: %v", file, err) + base.ErrorExit() } if p == "\n" { break // header ends with blank line } } - // In the importfile, if we find: - // $$\n (textual format): not supported anymore - // $$B\n (binary format) : import directly, then feed the lexer a dummy statement + // Expect $$B\n to signal binary import format. // look for $$ var c byte @@ -1304,41 +866,41 @@ func importfile(f *Val) *types.Pkg { var fingerprint goobj.FingerprintType switch c { case '\n': - yyerror("cannot import %s: old export format no longer supported (recompile library)", path_) + base.Errorf("cannot import %s: old export format no longer supported (recompile library)", path_) return nil case 'B': - if Debug_export != 0 { + if base.Debug.Export != 0 { fmt.Printf("importing %s (%s)\n", path_, file) } imp.ReadByte() // skip \n after $$B c, err = imp.ReadByte() if err != nil { - yyerror("import %s: reading input: %v", file, err) - errorexit() + base.Errorf("import %s: reading input: %v", file, err) + base.ErrorExit() } // Indexed format is distinguished by an 'i' byte, // whereas previous export formats started with 'c', 'd', or 'v'. if c != 'i' { - yyerror("import %s: unexpected package format byte: %v", file, c) - errorexit() + base.Errorf("import %s: unexpected package format byte: %v", file, c) + base.ErrorExit() } fingerprint = iimport(importpkg, imp) default: - yyerror("no import in %q", path_) - errorexit() + base.Errorf("no import in %q", path_) + base.ErrorExit() } // assume files move (get installed) so don't record the full path - if packageFile != nil { + if base.Flag.Cfg.PackageFile != nil { // If using a packageFile map, assume path_ can be recorded directly. - Ctxt.AddImport(path_, fingerprint) + base.Ctxt.AddImport(path_, fingerprint) } else { // For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a". - Ctxt.AddImport(file[len(file)-len(path_)-len(".a"):], fingerprint) + base.Ctxt.AddImport(file[len(file)-len(path_)-len(".a"):], fingerprint) } if importpkg.Height >= myheight { @@ -1360,21 +922,21 @@ func pkgnotused(lineno src.XPos, path string, name string) { elem = elem[i+1:] } if name == "" || elem == name { - yyerrorl(lineno, "imported and not used: %q", path) + base.ErrorfAt(lineno, "imported and not used: %q", path) } else { - yyerrorl(lineno, "imported and not used: %q as %s", path, name) + base.ErrorfAt(lineno, "imported and not used: %q as %s", path, name) } } func mkpackage(pkgname string) { - if localpkg.Name == "" { + if ir.LocalPkg.Name == "" { if pkgname == "_" { - yyerror("invalid package name _") + base.Errorf("invalid package name _") } - localpkg.Name = pkgname + ir.LocalPkg.Name = pkgname } else { - if pkgname != localpkg.Name { - yyerror("package %s; expected %s", pkgname, localpkg.Name) + if pkgname != ir.LocalPkg.Name { + base.Errorf("package %s; expected %s", pkgname, ir.LocalPkg.Name) } } } @@ -1387,19 +949,19 @@ func clearImports() { } var unused []importedPkg - for _, s := range localpkg.Syms { - n := asNode(s.Def) + for _, s := range ir.LocalPkg.Syms { + n := ir.AsNode(s.Def) if n == nil { continue } - if n.Op == OPACK { + if n.Op() == ir.OPACK { // throw away top-level package name left over // from previous file. // leave s->block set to cause redeclaration // errors if a conflicting top-level name is // introduced by a different file. - if !n.Name.Used() && nsyntaxerrors == 0 { - unused = append(unused, importedPkg{n.Pos, n.Name.Pkg.Path, s.Name}) + if !n.Name().Used() && base.SyntaxErrors() == 0 { + unused = append(unused, importedPkg{n.Pos(), n.Name().Pkg.Path, s.Name}) } s.Def = nil continue @@ -1407,9 +969,9 @@ func clearImports() { if IsAlias(s) { // throw away top-level name left over // from previous import . "x" - if n.Name != nil && n.Name.Pack != nil && !n.Name.Pack.Name.Used() && nsyntaxerrors == 0 { - unused = append(unused, importedPkg{n.Name.Pack.Pos, n.Name.Pack.Name.Pkg.Path, ""}) - n.Name.Pack.Name.SetUsed(true) + if n.Name() != nil && n.Name().Pack != nil && !n.Name().Pack.Name().Used() && base.SyntaxErrors() == 0 { + unused = append(unused, importedPkg{n.Name().Pack.Pos(), n.Name().Pack.Name().Pkg.Path, ""}) + n.Name().Pack.Name().SetUsed(true) } s.Def = nil continue @@ -1423,56 +985,13 @@ func clearImports() { } func IsAlias(sym *types.Sym) bool { - return sym.Def != nil && asNode(sym.Def).Sym != sym -} - -// By default, assume any debug flags are incompatible with concurrent -// compilation. A few are safe and potentially in common use for -// normal compiles, though; return true for those. -func concurrentFlagOk() bool { - // Report whether any debug flag that would prevent concurrent - // compilation is set, by zeroing out the allowed ones and then - // checking if the resulting struct is zero. - d := Debug - d.B = 0 // disable bounds checking - d.C = 0 // disable printing of columns in error messages - d.e = 0 // no limit on errors; errors all come from non-concurrent code - d.N = 0 // disable optimizations - d.l = 0 // disable inlining - d.w = 0 // all printing happens before compilation - d.W = 0 // all printing happens before compilation - d.S = 0 // printing disassembly happens at the end (but see concurrentBackendAllowed below) - - return d == DebugFlags{} -} - -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 Debug_vlog || debugstr != "" || debuglive > 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 + return sym.Def != nil && ir.AsNode(sym.Def).Sym() != sym } // recordFlags records the specified command-line flags to be placed // in the DWARF info. func recordFlags(flags ...string) { - if myimportpath == "" { + if base.Ctxt.Pkgpath == "" { // We can't record the flags if we don't know what the // package name is. return @@ -1515,30 +1034,27 @@ func recordFlags(flags ...string) { if cmd.Len() == 0 { return } - s := Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + myimportpath) + s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath) s.Type = objabi.SDWARFCUINFO // Sometimes (for example when building tests) we can link // together two package main archives. So allow dups. s.Set(obj.AttrDuplicateOK, true) - Ctxt.Data = append(Ctxt.Data, s) + base.Ctxt.Data = append(base.Ctxt.Data, s) s.P = cmd.Bytes()[1:] } // recordPackageName records the name of the package being // compiled, so that the linker can save it in the compile unit's DIE. func recordPackageName() { - s := Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + myimportpath) + s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath) s.Type = objabi.SDWARFCUINFO // Sometimes (for example when building tests) we can link // together two package main archives. So allow dups. s.Set(obj.AttrDuplicateOK, true) - Ctxt.Data = append(Ctxt.Data, s) - s.P = []byte(localpkg.Name) + base.Ctxt.Data = append(base.Ctxt.Data, s) + s.P = []byte(ir.LocalPkg.Name) } -// flag_lang is the language version we are compiling for, set by the -lang flag. -var flag_lang string - // currentLang returns the current language version. func currentLang() string { return fmt.Sprintf("go1.%d", goversion.Version) @@ -1563,9 +1079,9 @@ var langWant lang func langSupported(major, minor int, pkg *types.Pkg) bool { if pkg == nil { // TODO(mdempsky): Set Pkg for local types earlier. - pkg = localpkg + pkg = ir.LocalPkg } - if pkg != localpkg { + if pkg != ir.LocalPkg { // Assume imported packages passed type-checking. return true } @@ -1579,23 +1095,23 @@ func langSupported(major, minor int, pkg *types.Pkg) bool { // checkLang verifies that the -lang flag holds a valid value, and // exits if not. It initializes data used by langSupported. func checkLang() { - if flag_lang == "" { + if base.Flag.Lang == "" { return } var err error - langWant, err = parseLang(flag_lang) + langWant, err = parseLang(base.Flag.Lang) if err != nil { - log.Fatalf("invalid value %q for -lang: %v", flag_lang, err) + log.Fatalf("invalid value %q for -lang: %v", base.Flag.Lang, err) } - if def := currentLang(); flag_lang != def { + if def := currentLang(); base.Flag.Lang != def { defVers, err := parseLang(def) if err != nil { log.Fatalf("internal error parsing default lang %q: %v", def, err) } if langWant.major > defVers.major || (langWant.major == defVers.major && langWant.minor > defVers.minor) { - log.Fatalf("invalid value %q for -lang: max known version is %q", flag_lang, def) + log.Fatalf("invalid value %q for -lang: max known version is %q", base.Flag.Lang, def) } } } |