github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/base/flag.go (about) 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. 4 5 package base 6 7 import ( 8 "encoding/json" 9 "github.com/bir3/gocompiler/src/cmd/compile/flag" 10 "fmt" 11 "github.com/bir3/gocompiler/src/internal/buildcfg" 12 "github.com/bir3/gocompiler/src/internal/coverage" 13 "github.com/bir3/gocompiler/src/internal/platform" 14 "log" 15 "os" 16 "reflect" 17 "runtime" 18 "strings" 19 20 "github.com/bir3/gocompiler/src/cmd/internal/obj" 21 22 "github.com/bir3/gocompiler/src/cmd/compile/flag_objabi" 23 "github.com/bir3/gocompiler/src/cmd/internal/sys" 24 ) 25 26 func usage() { 27 fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n") 28 flag_objabi.Flagprint(os.Stderr) 29 Exit(2) 30 } 31 32 // Flag holds the parsed command-line flags. 33 // See ParseFlag for non-zero defaults. 34 var Flag CmdFlags 35 36 // A CountFlag is a counting integer flag. 37 // It accepts -name=value to set the value directly, 38 // but it also accepts -name with no =value to increment the count. 39 type CountFlag int 40 41 // CmdFlags defines the command-line flags (see var Flag). 42 // Each struct field is a different flag, by default named for the lower-case of the field name. 43 // If the flag name is a single letter, the default flag name is left upper-case. 44 // If the flag name is "Lower" followed by a single letter, the default flag name is the lower-case of the last letter. 45 // 46 // If this default flag name can't be made right, the `flag` struct tag can be used to replace it, 47 // but this should be done only in exceptional circumstances: it helps everyone if the flag name 48 // is obvious from the field name when the flag is used elsewhere in the compiler sources. 49 // The `flag:"-"` struct tag makes a field invisible to the flag logic and should also be used sparingly. 50 // 51 // Each field must have a `help` struct tag giving the flag help message. 52 // 53 // The allowed field types are bool, int, string, pointers to those (for values stored elsewhere), 54 // CountFlag (for a counting flag), and func(string) (for a flag that uses special code for parsing). 55 type CmdFlags struct { 56 // Single letters 57 B CountFlag "help:\"disable bounds checking\"" 58 C CountFlag "help:\"disable printing of columns in error messages\"" 59 D string "help:\"set relative `path` for local imports\"" 60 E CountFlag "help:\"debug symbol export\"" 61 I func(string) "help:\"add `directory` to import search path\"" 62 K CountFlag "help:\"debug missing line numbers\"" 63 L CountFlag "help:\"also show actual source file names in error messages for positions affected by //line directives\"" 64 N CountFlag "help:\"disable optimizations\"" 65 S CountFlag "help:\"print assembly listing\"" 66 // V is added by flag_objabi.AddVersionFlag 67 W CountFlag "help:\"debug parse tree after type checking\"" 68 69 LowerC int "help:\"concurrency during compilation (1 means no concurrency)\"" 70 LowerD flag.Value "help:\"enable debugging settings; try -d help\"" 71 LowerE CountFlag "help:\"no limit on number of errors reported\"" 72 LowerH CountFlag "help:\"halt on error\"" 73 LowerJ CountFlag "help:\"debug runtime-initialized variables\"" 74 LowerL CountFlag "help:\"disable inlining\"" 75 LowerM CountFlag "help:\"print optimization decisions\"" 76 LowerO string "help:\"write output to `file`\"" 77 LowerP *string "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below 78 LowerR CountFlag "help:\"debug generated wrappers\"" 79 LowerT bool "help:\"enable tracing for debugging the compiler\"" 80 LowerW CountFlag "help:\"debug type checking\"" 81 LowerV *bool "help:\"increase debug verbosity\"" 82 83 // Special characters 84 Percent CountFlag "flag:\"%\" help:\"debug non-static initializers\"" 85 CompilingRuntime bool "flag:\"+\" help:\"compiling runtime\"" 86 87 // Longer names 88 AsmHdr string "help:\"write assembly header to `file`\"" 89 ASan bool "help:\"build code compatible with C/C++ address sanitizer\"" 90 Bench string "help:\"append benchmark times to `file`\"" 91 BlockProfile string "help:\"write block profile to `file`\"" 92 BuildID string "help:\"record `id` as the build id in the export metadata\"" 93 CPUProfile string "help:\"write cpu profile to `file`\"" 94 Complete bool "help:\"compiling complete package (no C or assembly)\"" 95 ClobberDead bool "help:\"clobber dead stack slots (for debugging)\"" 96 ClobberDeadReg bool "help:\"clobber dead registers (for debugging)\"" 97 Dwarf bool "help:\"generate DWARF symbols\"" 98 DwarfBASEntries *bool "help:\"use base address selection entries in DWARF\"" // &Ctxt.UseBASEntries, set below 99 DwarfLocationLists *bool "help:\"add location lists to DWARF in optimized mode\"" // &Ctxt.Flag_locationlists, set below 100 Dynlink *bool "help:\"support references to Go symbols defined in other shared libraries\"" // &Ctxt.Flag_dynlink, set below 101 EmbedCfg func(string) "help:\"read go:embed configuration from `file`\"" 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 OldComparable bool "help:\"enable old comparable semantics\"" // TODO: remove for Go 1.21 128 PgoProfile string "help:\"read profile from `file`\"" 129 130 // Configuration derived from flags; not a flag itself. 131 Cfg struct { 132 Embed struct { // set by -embedcfg 133 Patterns map[string][]string 134 Files map[string]string 135 } 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 *coverage.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. 143 Instrumenting bool 144 } 145 } 146 147 // ParseFlags parses the command-line flags into Flag. 148 func ParseFlags() { 149 Flag.I = addImportDir 150 151 Flag.LowerC = runtime.GOMAXPROCS(0) 152 Flag.LowerD = flag_objabi.NewDebugFlag(&Debug, DebugSSA) 153 Flag.LowerP = &Ctxt.Pkgpath 154 Flag.LowerV = &Ctxt.Debugvlog 155 156 Flag.Dwarf = buildcfg.GOARCH != "wasm" 157 Flag.DwarfBASEntries = &Ctxt.UseBASEntries 158 Flag.DwarfLocationLists = &Ctxt.Flag_locationlists 159 *Flag.DwarfLocationLists = true 160 Flag.Dynlink = &Ctxt.Flag_dynlink 161 Flag.EmbedCfg = readEmbedCfg 162 Flag.GenDwarfInl = 2 163 Flag.ImportCfg = readImportCfg 164 Flag.CoverageCfg = readCoverageCfg 165 Flag.LinkShared = &Ctxt.Flag_linkshared 166 Flag.Shared = &Ctxt.Flag_shared 167 Flag.WB = true 168 169 Debug.ConcurrentOk = true 170 Debug.InlFuncsWithClosures = 1 171 Debug.InlStaticInit = 0 172 if buildcfg.Experiment.Unified { 173 Debug.Unified = 1 174 } 175 Debug.SyncFrames = -1 // disable sync markers by default 176 177 Debug.Checkptr = -1 // so we can tell whether it is set explicitly 178 179 Flag.Cfg.ImportMap = make(map[string]string) 180 181 flag_objabi.AddVersionFlag() // -V 182 registerFlags() 183 flag_objabi.Flagparse(usage) 184 185 if gcd := os.Getenv("GOCOMPILEDEBUG"); gcd != "" { 186 // This will only override the flags set in gcd; 187 // any others set on the command line remain set. 188 Flag.LowerD.Set(gcd) 189 } 190 191 if Debug.Gossahash != "" { 192 hashDebug = NewHashDebug("gosshash", Debug.Gossahash, nil) 193 } 194 195 if Debug.Fmahash != "" { 196 FmaHash = NewHashDebug("fmahash", Debug.Fmahash, nil) 197 } 198 199 if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) { 200 log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH) 201 } 202 if Flag.ASan && !platform.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) { 203 log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH) 204 } 205 if Flag.Race && !platform.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) { 206 log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH) 207 } 208 if (*Flag.Shared || *Flag.Dynlink || *Flag.LinkShared) && !Ctxt.Arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) { 209 log.Fatalf("%s/%s does not support -shared", buildcfg.GOOS, buildcfg.GOARCH) 210 } 211 parseSpectre(Flag.Spectre) // left as string for RecordFlags 212 213 Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared 214 Ctxt.Flag_optimize = Flag.N == 0 215 Ctxt.Debugasm = int(Flag.S) 216 Ctxt.Flag_maymorestack = Debug.MayMoreStack 217 Ctxt.Flag_noRefName = Debug.NoRefName != 0 218 219 if flag.NArg() < 1 { 220 usage() 221 } 222 223 if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() { 224 fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion) 225 Exit(2) 226 } 227 228 if *Flag.LowerP == "" { 229 *Flag.LowerP = obj.UnlinkablePkg 230 } 231 232 if Flag.LowerO == "" { 233 p := flag.Arg(0) 234 if i := strings.LastIndex(p, "/"); i >= 0 { 235 p = p[i+1:] 236 } 237 if runtime.GOOS == "windows" { 238 if i := strings.LastIndex(p, `\`); i >= 0 { 239 p = p[i+1:] 240 } 241 } 242 if i := strings.LastIndex(p, "."); i >= 0 { 243 p = p[:i] 244 } 245 suffix := ".o" 246 if Flag.Pack { 247 suffix = ".a" 248 } 249 Flag.LowerO = p + suffix 250 } 251 switch { 252 case Flag.Race && Flag.MSan: 253 log.Fatal("cannot use both -race and -msan") 254 case Flag.Race && Flag.ASan: 255 log.Fatal("cannot use both -race and -asan") 256 case Flag.MSan && Flag.ASan: 257 log.Fatal("cannot use both -msan and -asan") 258 } 259 if Flag.Race || Flag.MSan || Flag.ASan { 260 // -race, -msan and -asan imply -d=checkptr for now. 261 if Debug.Checkptr == -1 { // if not set explicitly 262 Debug.Checkptr = 1 263 } 264 } 265 266 if Flag.CompilingRuntime && Flag.N != 0 { 267 log.Fatal("cannot disable optimizations while compiling runtime") 268 } 269 if Flag.LowerC < 1 { 270 log.Fatalf("-c must be at least 1, got %d", Flag.LowerC) 271 } 272 if !concurrentBackendAllowed() { 273 Flag.LowerC = 1 274 } 275 276 if Flag.CompilingRuntime { 277 // Runtime can't use -d=checkptr, at least not yet. 278 Debug.Checkptr = 0 279 280 // Fuzzing the runtime isn't interesting either. 281 Debug.Libfuzzer = 0 282 } 283 284 if Debug.Checkptr == -1 { // if not set explicitly 285 Debug.Checkptr = 0 286 } 287 288 // set via a -d flag 289 Ctxt.Debugpcln = Debug.PCTab 290 } 291 292 // registerFlags adds flag registrations for all the fields in Flag. 293 // See the comment on type CmdFlags for the rules. 294 func registerFlags() { 295 var ( 296 boolType = reflect.TypeOf(bool(false)) 297 intType = reflect.TypeOf(int(0)) 298 stringType = reflect.TypeOf(string("")) 299 ptrBoolType = reflect.TypeOf(new(bool)) 300 ptrIntType = reflect.TypeOf(new(int)) 301 ptrStringType = reflect.TypeOf(new(string)) 302 countType = reflect.TypeOf(CountFlag(0)) 303 funcType = reflect.TypeOf((func(string))(nil)) 304 ) 305 306 v := reflect.ValueOf(&Flag).Elem() 307 t := v.Type() 308 for i := 0; i < t.NumField(); i++ { 309 f := t.Field(i) 310 if f.Name == "Cfg" { 311 continue 312 } 313 314 var name string 315 if len(f.Name) == 1 { 316 name = f.Name 317 } else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' { 318 name = string(rune(f.Name[5] + 'a' - 'A')) 319 } else { 320 name = strings.ToLower(f.Name) 321 } 322 if tag := f.Tag.Get("flag"); tag != "" { 323 name = tag 324 } 325 326 help := f.Tag.Get("help") 327 if help == "" { 328 panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name)) 329 } 330 331 if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() { 332 panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type)) 333 } 334 335 switch f.Type { 336 case boolType: 337 p := v.Field(i).Addr().Interface().(*bool) 338 flag.BoolVar(p, name, *p, help) 339 case intType: 340 p := v.Field(i).Addr().Interface().(*int) 341 flag.IntVar(p, name, *p, help) 342 case stringType: 343 p := v.Field(i).Addr().Interface().(*string) 344 flag.StringVar(p, name, *p, help) 345 case ptrBoolType: 346 p := v.Field(i).Interface().(*bool) 347 flag.BoolVar(p, name, *p, help) 348 case ptrIntType: 349 p := v.Field(i).Interface().(*int) 350 flag.IntVar(p, name, *p, help) 351 case ptrStringType: 352 p := v.Field(i).Interface().(*string) 353 flag.StringVar(p, name, *p, help) 354 case countType: 355 p := (*int)(v.Field(i).Addr().Interface().(*CountFlag)) 356 flag_objabi.Flagcount(name, help, p) 357 case funcType: 358 f := v.Field(i).Interface().(func(string)) 359 flag_objabi.Flagfn1(name, help, f) 360 default: 361 if val, ok := v.Field(i).Interface().(flag.Value); ok { 362 flag.Var(val, name, help) 363 } else { 364 panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type)) 365 } 366 } 367 } 368 } 369 370 // concurrentFlagOk reports whether the current compiler flags 371 // are compatible with concurrent compilation. 372 func concurrentFlagOk() bool { 373 // TODO(rsc): Many of these are fine. Remove them. 374 return Flag.Percent == 0 && 375 Flag.E == 0 && 376 Flag.K == 0 && 377 Flag.L == 0 && 378 Flag.LowerH == 0 && 379 Flag.LowerJ == 0 && 380 Flag.LowerM == 0 && 381 Flag.LowerR == 0 382 } 383 384 func concurrentBackendAllowed() bool { 385 if !concurrentFlagOk() { 386 return false 387 } 388 389 // Debug.S by itself is ok, because all printing occurs 390 // while writing the object file, and that is non-concurrent. 391 // Adding Debug_vlog, however, causes Debug.S to also print 392 // while flushing the plist, which happens concurrently. 393 if Ctxt.Debugvlog || !Debug.ConcurrentOk || Flag.Live > 0 { 394 return false 395 } 396 // TODO: Test and delete this condition. 397 if buildcfg.Experiment.FieldTrack { 398 return false 399 } 400 // TODO: fix races and enable the following flags 401 if Ctxt.Flag_dynlink || Flag.Race { 402 return false 403 } 404 return true 405 } 406 407 func addImportDir(dir string) { 408 if dir != "" { 409 Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir) 410 } 411 } 412 413 func readImportCfg(file string) { 414 if Flag.Cfg.ImportMap == nil { 415 Flag.Cfg.ImportMap = make(map[string]string) 416 } 417 Flag.Cfg.PackageFile = map[string]string{} 418 data, err := os.ReadFile(file) 419 if err != nil { 420 log.Fatalf("-importcfg: %v", err) 421 } 422 423 for lineNum, line := range strings.Split(string(data), "\n") { 424 lineNum++ // 1-based 425 line = strings.TrimSpace(line) 426 if line == "" || strings.HasPrefix(line, "#") { 427 continue 428 } 429 430 var verb, args string 431 if i := strings.Index(line, " "); i < 0 { 432 verb = line 433 } else { 434 verb, args = line[:i], strings.TrimSpace(line[i+1:]) 435 } 436 var before, after string 437 if i := strings.Index(args, "="); i >= 0 { 438 before, after = args[:i], args[i+1:] 439 } 440 switch verb { 441 default: 442 log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb) 443 case "importmap": 444 if before == "" || after == "" { 445 log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum) 446 } 447 Flag.Cfg.ImportMap[before] = after 448 case "packagefile": 449 if before == "" || after == "" { 450 log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum) 451 } 452 Flag.Cfg.PackageFile[before] = after 453 } 454 } 455 } 456 457 func readCoverageCfg(file string) { 458 var cfg coverage.CoverFixupConfig 459 data, err := os.ReadFile(file) 460 if err != nil { 461 log.Fatalf("-coveragecfg: %v", err) 462 } 463 if err := json.Unmarshal(data, &cfg); err != nil { 464 log.Fatalf("error reading -coveragecfg file %q: %v", file, err) 465 } 466 Flag.Cfg.CoverageInfo = &cfg 467 } 468 469 func readEmbedCfg(file string) { 470 data, err := os.ReadFile(file) 471 if err != nil { 472 log.Fatalf("-embedcfg: %v", err) 473 } 474 if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil { 475 log.Fatalf("%s: %v", file, err) 476 } 477 if Flag.Cfg.Embed.Patterns == nil { 478 log.Fatalf("%s: invalid embedcfg: missing Patterns", file) 479 } 480 if Flag.Cfg.Embed.Files == nil { 481 log.Fatalf("%s: invalid embedcfg: missing Files", file) 482 } 483 } 484 485 // parseSpectre parses the spectre configuration from the string s. 486 func parseSpectre(s string) { 487 for _, f := range strings.Split(s, ",") { 488 f = strings.TrimSpace(f) 489 switch f { 490 default: 491 log.Fatalf("unknown setting -spectre=%s", f) 492 case "": 493 // nothing 494 case "all": 495 Flag.Cfg.SpectreIndex = true 496 Ctxt.Retpoline = true 497 case "index": 498 Flag.Cfg.SpectreIndex = true 499 case "ret": 500 Ctxt.Retpoline = true 501 } 502 } 503 504 if Flag.Cfg.SpectreIndex { 505 switch buildcfg.GOARCH { 506 case "amd64": 507 // ok 508 default: 509 log.Fatalf("GOARCH=%s does not support -spectre=index", buildcfg.GOARCH) 510 } 511 } 512 }