1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
12 "internal/coverage/covcmd"
26 fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
27 objabi.Flagprint(os.Stderr)
31 // Flag holds the parsed command-line flags.
32 // See ParseFlag for non-zero defaults.
35 // A CountFlag is a counting integer flag.
36 // It accepts -name=value to set the value directly,
37 // but it also accepts -name with no =value to increment the count.
40 // CmdFlags defines the command-line flags (see var Flag).
41 // Each struct field is a different flag, by default named for the lower-case of the field name.
42 // If the flag name is a single letter, the default flag name is left upper-case.
43 // If the flag name is "Lower" followed by a single letter, the default flag name is the lower-case of the last letter.
45 // If this default flag name can't be made right, the `flag` struct tag can be used to replace it,
46 // but this should be done only in exceptional circumstances: it helps everyone if the flag name
47 // is obvious from the field name when the flag is used elsewhere in the compiler sources.
48 // The `flag:"-"` struct tag makes a field invisible to the flag logic and should also be used sparingly.
50 // Each field must have a `help` struct tag giving the flag help message.
52 // The allowed field types are bool, int, string, pointers to those (for values stored elsewhere),
53 // CountFlag (for a counting flag), and func(string) (for a flag that uses special code for parsing).
54 type CmdFlags struct {
56 B CountFlag "help:\"disable bounds checking\""
57 C CountFlag "help:\"disable printing of columns in error messages\""
58 D string "help:\"set relative `path` for local imports\""
59 E CountFlag "help:\"debug symbol export\""
60 I func(string) "help:\"add `directory` to import search path\""
61 K CountFlag "help:\"debug missing line numbers\""
62 L CountFlag "help:\"also show actual source file names in error messages for positions affected by //line directives\""
63 N CountFlag "help:\"disable optimizations\""
64 S CountFlag "help:\"print assembly listing\""
65 // V is added by objabi.AddVersionFlag
66 W CountFlag "help:\"debug parse tree after type checking\""
68 LowerC int "help:\"concurrency during compilation (1 means no concurrency)\""
69 LowerD flag.Value "help:\"enable debugging settings; try -d help\""
70 LowerE CountFlag "help:\"no limit on number of errors reported\""
71 LowerH CountFlag "help:\"halt on error\""
72 LowerJ CountFlag "help:\"debug runtime-initialized variables\""
73 LowerL CountFlag "help:\"disable inlining\""
74 LowerM CountFlag "help:\"print optimization decisions\""
75 LowerO string "help:\"write output to `file`\""
76 LowerP *string "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below
77 LowerR CountFlag "help:\"debug generated wrappers\""
78 LowerT bool "help:\"enable tracing for debugging the compiler\""
79 LowerW CountFlag "help:\"debug type checking\""
80 LowerV *bool "help:\"increase debug verbosity\""
83 Percent CountFlag "flag:\"%\" help:\"debug non-static initializers\""
84 CompilingRuntime bool "flag:\"+\" help:\"compiling runtime\""
87 AsmHdr string "help:\"write assembly header to `file`\""
88 ASan bool "help:\"build code compatible with C/C++ address sanitizer\""
89 Bench string "help:\"append benchmark times to `file`\""
90 BlockProfile string "help:\"write block profile to `file`\""
91 BuildID string "help:\"record `id` as the build id in the export metadata\""
92 CPUProfile string "help:\"write cpu profile to `file`\""
93 Complete bool "help:\"compiling complete package (no C or assembly)\""
94 ClobberDead bool "help:\"clobber dead stack slots (for debugging)\""
95 ClobberDeadReg bool "help:\"clobber dead registers (for debugging)\""
96 Dwarf bool "help:\"generate DWARF symbols\""
97 DwarfBASEntries *bool "help:\"use base address selection entries in DWARF\"" // &Ctxt.UseBASEntries, set below
98 DwarfLocationLists *bool "help:\"add location lists to DWARF in optimized mode\"" // &Ctxt.Flag_locationlists, set below
99 Dynlink *bool "help:\"support references to Go symbols defined in other shared libraries\"" // &Ctxt.Flag_dynlink, set below
100 EmbedCfg func(string) "help:\"read go:embed configuration from `file`\""
101 Env func(string) "help:\"add `definition` of the form key=value to environment\""
102 GenDwarfInl int "help:\"generate DWARF inline info records\"" // 0=disabled, 1=funcs, 2=funcs+formals/locals
103 GoVersion string "help:\"required version of the runtime\""
104 ImportCfg func(string) "help:\"read import configuration from `file`\""
105 InstallSuffix string "help:\"set pkg directory `suffix`\""
106 JSON string "help:\"version,file for JSON compiler/optimizer detail output\""
107 Lang string "help:\"Go language version source code expects\""
108 LinkObj string "help:\"write linker-specific object to `file`\""
109 LinkShared *bool "help:\"generate code that will be linked against Go shared libraries\"" // &Ctxt.Flag_linkshared, set below
110 Live CountFlag "help:\"debug liveness analysis\""
111 MSan bool "help:\"build code compatible with C/C++ memory sanitizer\""
112 MemProfile string "help:\"write memory profile to `file`\""
113 MemProfileRate int "help:\"set runtime.MemProfileRate to `rate`\""
114 MutexProfile string "help:\"write mutex profile to `file`\""
115 NoLocalImports bool "help:\"reject local (relative) imports\""
116 CoverageCfg func(string) "help:\"read coverage configuration from `file`\""
117 Pack bool "help:\"write to file.a instead of file.o\""
118 Race bool "help:\"enable race detector\""
119 Shared *bool "help:\"generate code that can be linked into a shared library\"" // &Ctxt.Flag_shared, set below
120 SmallFrames bool "help:\"reduce the size limit for stack allocated objects\"" // small stacks, to diagnose GC latency; see golang.org/issue/27732
121 Spectre string "help:\"enable spectre mitigations in `list` (all, index, ret)\""
122 Std bool "help:\"compiling standard library\""
123 SymABIs string "help:\"read symbol ABIs from `file`\""
124 TraceProfile string "help:\"write an execution trace to `file`\""
125 TrimPath string "help:\"remove `prefix` from recorded source file paths\""
126 WB bool "help:\"enable write barrier\"" // TODO: remove
127 PgoProfile string "help:\"read profile from `file`\""
128 ErrorURL bool "help:\"print explanatory URL with error message if applicable\""
130 // Configuration derived from flags; not a flag itself.
132 Embed struct { // set by -embedcfg
133 Patterns map[string][]string
134 Files map[string]string
136 ImportDirs []string // appended to by -I
137 ImportMap map[string]string // set by -importcfg
138 PackageFile map[string]string // set by -importcfg; nil means not in use
139 CoverageInfo *covcmd.CoverFixupConfig // set by -coveragecfg
140 SpectreIndex bool // set by -spectre=index or -spectre=all
141 // Whether we are adding any sort of code instrumentation, such as
142 // when the race detector is enabled.
147 func addEnv(s string) {
148 i := strings.Index(s, "=")
150 log.Fatal("-env argument must be of the form key=value")
152 os.Setenv(s[:i], s[i+1:])
155 // ParseFlags parses the command-line flags into Flag.
157 Flag.I = addImportDir
159 Flag.LowerC = runtime.GOMAXPROCS(0)
160 Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA)
161 Flag.LowerP = &Ctxt.Pkgpath
162 Flag.LowerV = &Ctxt.Debugvlog
164 Flag.Dwarf = buildcfg.GOARCH != "wasm"
165 Flag.DwarfBASEntries = &Ctxt.UseBASEntries
166 Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
167 *Flag.DwarfLocationLists = true
168 Flag.Dynlink = &Ctxt.Flag_dynlink
169 Flag.EmbedCfg = readEmbedCfg
172 Flag.ImportCfg = readImportCfg
173 Flag.CoverageCfg = readCoverageCfg
174 Flag.LinkShared = &Ctxt.Flag_linkshared
175 Flag.Shared = &Ctxt.Flag_shared
178 Debug.ConcurrentOk = true
179 Debug.InlFuncsWithClosures = 1
180 Debug.InlStaticInit = 1
182 Debug.PGODevirtualize = 1
183 Debug.SyncFrames = -1 // disable sync markers by default
185 Debug.Checkptr = -1 // so we can tell whether it is set explicitly
187 Flag.Cfg.ImportMap = make(map[string]string)
189 objabi.AddVersionFlag() // -V
191 objabi.Flagparse(usage)
193 if gcd := os.Getenv("GOCOMPILEDEBUG"); gcd != "" {
194 // This will only override the flags set in gcd;
195 // any others set on the command line remain set.
199 if Debug.Gossahash != "" {
200 hashDebug = NewHashDebug("gossahash", Debug.Gossahash, nil)
203 // Three inputs govern loop iteration variable rewriting, hash, experiment, flag.
204 // The loop variable rewriting is:
205 // IF non-empty hash, then hash determines behavior (function+line match) (*)
206 // ELSE IF experiment and flag==0, then experiment (set flag=1)
207 // ELSE flag (note that build sets flag per-package), with behaviors:
208 // -1 => no change to behavior.
209 // 0 => no change to behavior (unless non-empty hash, see above)
210 // 1 => apply change to likely-iteration-variable-escaping loops
211 // 2 => apply change, log results
212 // 11 => apply change EVERYWHERE, do not log results (for debugging/benchmarking)
213 // 12 => apply change EVERYWHERE, log results (for debugging/benchmarking)
215 // The expected uses of the these inputs are, in believed most-likely to least likely:
216 // GOEXPERIMENT=loopvar -- apply change to entire application
217 // -gcflags=some_package=-d=loopvar=1 -- apply change to some_package (**)
218 // -gcflags=some_package=-d=loopvar=2 -- apply change to some_package, log it
219 // GOEXPERIMENT=loopvar -gcflags=some_package=-d=loopvar=-1 -- apply change to all but one package
220 // GOCOMPILEDEBUG=loopvarhash=... -- search for failure cause
222 // (*) For debugging purposes, providing loopvar flag >= 11 will expand the hash-eligible set of loops to all.
223 // (**) Loop semantics, changed or not, follow code from a package when it is inlined; that is, the behavior
224 // of an application compiled with partially modified loop semantics does not depend on inlining.
226 if Debug.LoopVarHash != "" {
227 // This first little bit controls the inputs for debug-hash-matching.
228 mostInlineOnly := true
229 if strings.HasPrefix(Debug.LoopVarHash, "IL") {
230 // When hash-searching on a position that is an inline site, default is to use the
231 // most-inlined position only. This makes the hash faster, plus there's no point
232 // reporting a problem with all the inlining; there's only one copy of the source.
233 // However, if for some reason you wanted it per-site, you can get this. (The default
234 // hash-search behavior for compiler debugging is at an inline site.)
235 Debug.LoopVarHash = Debug.LoopVarHash[2:]
236 mostInlineOnly = false
238 // end of testing trickiness
239 LoopVarHash = NewHashDebug("loopvarhash", Debug.LoopVarHash, nil)
240 if Debug.LoopVar < 11 { // >= 11 means all loops are rewrite-eligible
241 Debug.LoopVar = 1 // 1 means those loops that syntactically escape their dcl vars are eligible.
243 LoopVarHash.SetInlineSuffixOnly(mostInlineOnly)
244 } else if buildcfg.Experiment.LoopVar && Debug.LoopVar == 0 {
248 if Debug.Fmahash != "" {
249 FmaHash = NewHashDebug("fmahash", Debug.Fmahash, nil)
252 if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
253 log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
255 if Flag.ASan && !platform.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
256 log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
258 if Flag.Race && !platform.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
259 log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
261 if (*Flag.Shared || *Flag.Dynlink || *Flag.LinkShared) && !Ctxt.Arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.Loong64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) {
262 log.Fatalf("%s/%s does not support -shared", buildcfg.GOOS, buildcfg.GOARCH)
264 parseSpectre(Flag.Spectre) // left as string for RecordFlags
266 Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
267 Ctxt.Flag_optimize = Flag.N == 0
268 Ctxt.Debugasm = int(Flag.S)
269 Ctxt.Flag_maymorestack = Debug.MayMoreStack
270 Ctxt.Flag_noRefName = Debug.NoRefName != 0
276 if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
277 fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
281 if *Flag.LowerP == "" {
282 *Flag.LowerP = obj.UnlinkablePkg
285 if Flag.LowerO == "" {
287 if i := strings.LastIndex(p, "/"); i >= 0 {
290 if runtime.GOOS == "windows" {
291 if i := strings.LastIndex(p, `\`); i >= 0 {
295 if i := strings.LastIndex(p, "."); i >= 0 {
302 Flag.LowerO = p + suffix
305 case Flag.Race && Flag.MSan:
306 log.Fatal("cannot use both -race and -msan")
307 case Flag.Race && Flag.ASan:
308 log.Fatal("cannot use both -race and -asan")
309 case Flag.MSan && Flag.ASan:
310 log.Fatal("cannot use both -msan and -asan")
312 if Flag.Race || Flag.MSan || Flag.ASan {
313 // -race, -msan and -asan imply -d=checkptr for now.
314 if Debug.Checkptr == -1 { // if not set explicitly
319 if Flag.CompilingRuntime && Flag.N != 0 {
320 log.Fatal("cannot disable optimizations while compiling runtime")
323 log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
325 if !concurrentBackendAllowed() {
329 if Flag.CompilingRuntime {
330 // Runtime can't use -d=checkptr, at least not yet.
333 // Fuzzing the runtime isn't interesting either.
337 if Debug.Checkptr == -1 { // if not set explicitly
342 Ctxt.Debugpcln = Debug.PCTab
345 // registerFlags adds flag registrations for all the fields in Flag.
346 // See the comment on type CmdFlags for the rules.
347 func registerFlags() {
349 boolType = reflect.TypeOf(bool(false))
350 intType = reflect.TypeOf(int(0))
351 stringType = reflect.TypeOf(string(""))
352 ptrBoolType = reflect.TypeOf(new(bool))
353 ptrIntType = reflect.TypeOf(new(int))
354 ptrStringType = reflect.TypeOf(new(string))
355 countType = reflect.TypeOf(CountFlag(0))
356 funcType = reflect.TypeOf((func(string))(nil))
359 v := reflect.ValueOf(&Flag).Elem()
361 for i := 0; i < t.NumField(); i++ {
368 if len(f.Name) == 1 {
370 } else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
371 name = string(rune(f.Name[5] + 'a' - 'A'))
373 name = strings.ToLower(f.Name)
375 if tag := f.Tag.Get("flag"); tag != "" {
379 help := f.Tag.Get("help")
381 panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
384 if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
385 panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
390 p := v.Field(i).Addr().Interface().(*bool)
391 flag.BoolVar(p, name, *p, help)
393 p := v.Field(i).Addr().Interface().(*int)
394 flag.IntVar(p, name, *p, help)
396 p := v.Field(i).Addr().Interface().(*string)
397 flag.StringVar(p, name, *p, help)
399 p := v.Field(i).Interface().(*bool)
400 flag.BoolVar(p, name, *p, help)
402 p := v.Field(i).Interface().(*int)
403 flag.IntVar(p, name, *p, help)
405 p := v.Field(i).Interface().(*string)
406 flag.StringVar(p, name, *p, help)
408 p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
409 objabi.Flagcount(name, help, p)
411 f := v.Field(i).Interface().(func(string))
412 objabi.Flagfn1(name, help, f)
414 if val, ok := v.Field(i).Interface().(flag.Value); ok {
415 flag.Var(val, name, help)
417 panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
423 // concurrentFlagOk reports whether the current compiler flags
424 // are compatible with concurrent compilation.
425 func concurrentFlagOk() bool {
426 // TODO(rsc): Many of these are fine. Remove them.
427 return Flag.Percent == 0 &&
437 func concurrentBackendAllowed() bool {
438 if !concurrentFlagOk() {
442 // Debug.S by itself is ok, because all printing occurs
443 // while writing the object file, and that is non-concurrent.
444 // Adding Debug_vlog, however, causes Debug.S to also print
445 // while flushing the plist, which happens concurrently.
446 if Ctxt.Debugvlog || !Debug.ConcurrentOk || Flag.Live > 0 {
449 // TODO: Test and delete this condition.
450 if buildcfg.Experiment.FieldTrack {
453 // TODO: fix races and enable the following flags
454 if Ctxt.Flag_dynlink || Flag.Race {
460 func addImportDir(dir string) {
462 Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
466 func readImportCfg(file string) {
467 if Flag.Cfg.ImportMap == nil {
468 Flag.Cfg.ImportMap = make(map[string]string)
470 Flag.Cfg.PackageFile = map[string]string{}
471 data, err := os.ReadFile(file)
473 log.Fatalf("-importcfg: %v", err)
476 for lineNum, line := range strings.Split(string(data), "\n") {
478 line = strings.TrimSpace(line)
479 if line == "" || strings.HasPrefix(line, "#") {
483 verb, args, found := strings.Cut(line, " ")
485 args = strings.TrimSpace(args)
487 before, after, hasEq := strings.Cut(args, "=")
491 log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
493 if !hasEq || before == "" || after == "" {
494 log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
496 Flag.Cfg.ImportMap[before] = after
498 if !hasEq || before == "" || after == "" {
499 log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
501 Flag.Cfg.PackageFile[before] = after
506 func readCoverageCfg(file string) {
507 var cfg covcmd.CoverFixupConfig
508 data, err := os.ReadFile(file)
510 log.Fatalf("-coveragecfg: %v", err)
512 if err := json.Unmarshal(data, &cfg); err != nil {
513 log.Fatalf("error reading -coveragecfg file %q: %v", file, err)
515 Flag.Cfg.CoverageInfo = &cfg
518 func readEmbedCfg(file string) {
519 data, err := os.ReadFile(file)
521 log.Fatalf("-embedcfg: %v", err)
523 if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
524 log.Fatalf("%s: %v", file, err)
526 if Flag.Cfg.Embed.Patterns == nil {
527 log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
529 if Flag.Cfg.Embed.Files == nil {
530 log.Fatalf("%s: invalid embedcfg: missing Files", file)
534 // parseSpectre parses the spectre configuration from the string s.
535 func parseSpectre(s string) {
536 for _, f := range strings.Split(s, ",") {
537 f = strings.TrimSpace(f)
540 log.Fatalf("unknown setting -spectre=%s", f)
544 Flag.Cfg.SpectreIndex = true
545 Ctxt.Retpoline = true
547 Flag.Cfg.SpectreIndex = true
549 Ctxt.Retpoline = true
553 if Flag.Cfg.SpectreIndex {
554 switch buildcfg.GOARCH {
558 log.Fatalf("GOARCH=%s does not support -spectre=index", buildcfg.GOARCH)