1
2
3
4
5 package base
6
7 import (
8 "cmd/internal/cov/covcmd"
9 "cmd/internal/telemetry/counter"
10 "encoding/json"
11 "flag"
12 "fmt"
13 "internal/buildcfg"
14 "internal/platform"
15 "log"
16 "os"
17 "reflect"
18 "runtime"
19 "strings"
20
21 "cmd/internal/obj"
22 "cmd/internal/objabi"
23 "cmd/internal/sys"
24 )
25
26 func usage() {
27 fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
28 objabi.Flagprint(os.Stderr)
29 Exit(2)
30 }
31
32
33
34 var Flag CmdFlags
35
36
37
38
39 type CountFlag int
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 type CmdFlags struct {
56
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
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`\""
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 LowerU CountFlag "help:\"emit unsorted warnings/errors\""
82 LowerV *bool "help:\"increase debug verbosity\""
83
84
85 Percent CountFlag "flag:\"%\" help:\"debug non-static initializers\""
86 CompilingRuntime bool "flag:\"+\" help:\"compiling runtime\""
87
88
89 AsmHdr string "help:\"write assembly header to `file`\""
90 ASan bool "help:\"build code compatible with C/C++ address sanitizer\""
91 Bench string "help:\"append benchmark times to `file`\""
92 BlockProfile string "help:\"write block profile to `file`\""
93 BuildID string "help:\"record `id` as the build id in the export metadata\""
94 CPUProfile string "help:\"write cpu profile to `file`\""
95 Complete bool "help:\"compiling complete package (no C or assembly)\""
96 ClobberDead bool "help:\"clobber dead stack slots (for debugging)\""
97 ClobberDeadReg bool "help:\"clobber dead registers (for debugging)\""
98 Dwarf bool "help:\"generate DWARF symbols\""
99 DwarfBASEntries *bool "help:\"use base address selection entries in DWARF\""
100 DwarfLocationLists *bool "help:\"add location lists to DWARF in optimized mode\""
101 Dynlink *bool "help:\"support references to Go symbols defined in other shared libraries\""
102 EmbedCfg func(string) "help:\"read go:embed configuration from `file`\""
103 Env func(string) "help:\"add `definition` of the form key=value to environment\""
104 ExportFD int "help:\"write a byte to file descriptor `fd` once the export data has been written\""
105 GenDwarfInl int "help:\"generate DWARF inline info records\""
106 GoVersion string "help:\"required version of the runtime\""
107 ImportCfg func(string) "help:\"read import configuration from `file`\""
108 InstallSuffix string "help:\"set pkg directory `suffix`\""
109 JSON string "help:\"version,file for JSON compiler/optimizer detail output\""
110 Lang string "help:\"Go language version source code expects\""
111 LinkObj string "help:\"write linker-specific object to `file`\""
112 LinkShared *bool "help:\"generate code that will be linked against Go shared libraries\""
113 Live CountFlag "help:\"debug liveness analysis\""
114 MSan bool "help:\"build code compatible with C/C++ memory sanitizer\""
115 MemProfile string "help:\"write memory profile to `file`\""
116 MemProfileRate int "help:\"set runtime.MemProfileRate to `rate`\""
117 MutexProfile string "help:\"write mutex profile to `file`\""
118 NoLocalImports bool "help:\"reject local (relative) imports\""
119 CoverageCfg func(string) "help:\"read coverage configuration from `file`\""
120 Pack bool "help:\"write to file.a instead of file.o\""
121 Race bool "help:\"enable race detector\""
122 Shared *bool "help:\"generate code that can be linked into a shared library\""
123 SmallFrames bool "help:\"reduce the size limit for stack allocated objects\""
124 Spectre string "help:\"enable spectre mitigations in `list` (all, index, ret)\""
125 Stats bool "help:\"print compiler statistics\""
126 Std bool "help:\"compiling standard library\""
127 SymABIs string "help:\"read symbol ABIs from `file`\""
128 TraceProfile string "help:\"write an execution trace to `file`\""
129 TrimPath string "help:\"remove `prefix` from recorded source file paths\""
130 WB bool "help:\"enable write barrier\""
131 PgoProfile string "help:\"read profile or pre-process profile from `file`\""
132 ErrorURL bool "help:\"print explanatory URL with error message if applicable\""
133
134
135 Cfg struct {
136 Embed struct {
137 Patterns map[string][]string
138 Files map[string]string
139 }
140 ImportDirs []string
141 ImportMap map[string]string
142 PackageFile map[string]string
143 CoverageInfo *covcmd.CoverFixupConfig
144 SpectreIndex bool
145
146
147 Instrumenting bool
148 }
149 }
150
151 func addEnv(s string) {
152 i := strings.Index(s, "=")
153 if i < 0 {
154 log.Fatal("-env argument must be of the form key=value")
155 }
156 os.Setenv(s[:i], s[i+1:])
157 }
158
159
160 func ParseFlags() {
161 Flag.I = addImportDir
162
163 Flag.LowerC = runtime.GOMAXPROCS(0)
164 Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA)
165 Flag.LowerP = &Ctxt.Pkgpath
166 Flag.LowerV = &Ctxt.Debugvlog
167
168 Flag.Dwarf = buildcfg.GOARCH != "wasm"
169 Flag.DwarfBASEntries = &Ctxt.UseBASEntries
170 Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
171 *Flag.DwarfLocationLists = true
172 Flag.Dynlink = &Ctxt.Flag_dynlink
173 Flag.EmbedCfg = readEmbedCfg
174 Flag.Env = addEnv
175 Flag.GenDwarfInl = 2
176 Flag.ImportCfg = readImportCfg
177 Flag.CoverageCfg = readCoverageCfg
178 Flag.LinkShared = &Ctxt.Flag_linkshared
179 Flag.Shared = &Ctxt.Flag_shared
180 Flag.WB = true
181
182 Debug.ConcurrentOk = true
183 Debug.CompressInstructions = 1
184 Debug.MaxShapeLen = 500
185 Debug.AlignHot = 1
186 Debug.InlFuncsWithClosures = 1
187 Debug.InlStaticInit = 1
188 Debug.FreeAppend = 1
189 Debug.PGOInline = 1
190 Debug.PGODevirtualize = 2
191 Debug.SyncFrames = -1
192 Debug.VariableMakeThreshold = 32
193 Debug.ZeroCopy = 1
194 Debug.RangeFuncCheck = 1
195 Debug.MergeLocals = 1
196 Debug.RewriteResults = 1
197
198 Debug.Checkptr = -1
199
200 Flag.Cfg.ImportMap = make(map[string]string)
201
202 objabi.AddVersionFlag()
203 registerFlags()
204 objabi.Flagparse(usage)
205 counter.CountFlags("compile/flag:", *flag.CommandLine)
206
207 if gcd := os.Getenv("GOCOMPILEDEBUG"); gcd != "" {
208
209
210 Flag.LowerD.Set(gcd)
211 }
212
213 if Debug.Gossahash != "" {
214 hashDebug = NewHashDebug("gossahash", Debug.Gossahash, nil)
215 }
216 obj.SetFIPSDebugHash(Debug.FIPSHash)
217
218
219
220 if Flag.Std && objabi.LookupPkgSpecial(Ctxt.Pkgpath).Runtime {
221 Flag.CompilingRuntime = true
222 }
223
224 Ctxt.Std = Flag.Std
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249 if Debug.LoopVarHash != "" {
250
251 mostInlineOnly := true
252 if strings.HasPrefix(Debug.LoopVarHash, "IL") {
253
254
255
256
257
258 Debug.LoopVarHash = Debug.LoopVarHash[2:]
259 mostInlineOnly = false
260 }
261
262 LoopVarHash = NewHashDebug("loopvarhash", Debug.LoopVarHash, nil)
263 if Debug.LoopVar < 11 {
264 Debug.LoopVar = 1
265 }
266 LoopVarHash.SetInlineSuffixOnly(mostInlineOnly)
267 } else if buildcfg.Experiment.LoopVar && Debug.LoopVar == 0 {
268 Debug.LoopVar = 1
269 }
270
271 if Debug.Converthash != "" {
272 ConvertHash = NewHashDebug("converthash", Debug.Converthash, nil)
273 } else {
274
275 ConvertHash = NewHashDebug("converthash", "qn", nil)
276 }
277 if Debug.Fmahash != "" {
278 FmaHash = NewHashDebug("fmahash", Debug.Fmahash, nil)
279 }
280 if Debug.PGOHash != "" {
281 PGOHash = NewHashDebug("pgohash", Debug.PGOHash, nil)
282 }
283 if Debug.LiteralAllocHash != "" {
284 LiteralAllocHash = NewHashDebug("literalalloc", Debug.LiteralAllocHash, nil)
285 }
286
287 if Debug.MergeLocalsHash != "" {
288 MergeLocalsHash = NewHashDebug("mergelocals", Debug.MergeLocalsHash, nil)
289 }
290 if Debug.VariableMakeHash != "" {
291 VariableMakeHash = NewHashDebug("variablemake", Debug.VariableMakeHash, nil)
292 }
293
294 if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
295 log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
296 }
297 if Flag.ASan && !platform.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
298 log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
299 }
300 if Flag.Race && !platform.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
301 log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
302 }
303 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) {
304 log.Fatalf("%s/%s does not support -shared", buildcfg.GOOS, buildcfg.GOARCH)
305 }
306 parseSpectre(Flag.Spectre)
307
308 Ctxt.CompressInstructions = Debug.CompressInstructions != 0
309 Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
310 Ctxt.Flag_optimize = Flag.N == 0
311 Ctxt.Debugasm = int(Flag.S)
312 Ctxt.Flag_maymorestack = Debug.MayMoreStack
313 Ctxt.Flag_noRefName = Debug.NoRefName != 0
314
315 if flag.NArg() < 1 {
316 usage()
317 }
318
319 if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
320 fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
321 Exit(2)
322 }
323
324 if *Flag.LowerP == "" {
325 *Flag.LowerP = obj.UnlinkablePkg
326 }
327
328 if Flag.LowerO == "" {
329 p := flag.Arg(0)
330 if i := strings.LastIndex(p, "/"); i >= 0 {
331 p = p[i+1:]
332 }
333 if runtime.GOOS == "windows" {
334 if i := strings.LastIndex(p, `\`); i >= 0 {
335 p = p[i+1:]
336 }
337 }
338 if i := strings.LastIndex(p, "."); i >= 0 {
339 p = p[:i]
340 }
341 suffix := ".o"
342 if Flag.Pack {
343 suffix = ".a"
344 }
345 Flag.LowerO = p + suffix
346 }
347 switch {
348 case Flag.Race && Flag.MSan:
349 log.Fatal("cannot use both -race and -msan")
350 case Flag.Race && Flag.ASan:
351 log.Fatal("cannot use both -race and -asan")
352 case Flag.MSan && Flag.ASan:
353 log.Fatal("cannot use both -msan and -asan")
354 }
355 if Flag.Race || Flag.MSan || Flag.ASan {
356
357 if Debug.Checkptr == -1 {
358 Debug.Checkptr = 1
359 }
360 }
361
362 if Flag.LowerC < 1 {
363 log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
364 }
365 if Flag.ExportFD > 0 && Flag.LinkObj == "" {
366 log.Fatalf("-exportfd requires -linkobj")
367 }
368 if !concurrentBackendAllowed() {
369 Flag.LowerC = 1
370 }
371
372 if Flag.CompilingRuntime {
373
374
375 Flag.N = 0
376 Ctxt.Flag_optimize = true
377
378
379 Debug.Checkptr = 0
380
381
382 Debug.Libfuzzer = 0
383 }
384
385 if len(Flag.Cfg.ImportDirs) > 0 && Flag.Cfg.PackageFile != nil {
386 log.Fatalf("cannot use both -I and -importcfg")
387 }
388
389 if Debug.Checkptr == -1 {
390 Debug.Checkptr = 0
391 }
392
393
394 Ctxt.Debugpcln = Debug.PCTab
395
396
397 if buildcfg.GOOS == "plan9" && buildcfg.GOARCH == "386" {
398 Debug.AlignHot = 0
399 }
400 }
401
402
403
404 func registerFlags() {
405 var (
406 boolType = reflect.TypeFor[bool]()
407 intType = reflect.TypeFor[int]()
408 stringType = reflect.TypeFor[string]()
409 ptrBoolType = reflect.TypeFor[*bool]()
410 ptrIntType = reflect.TypeFor[*int]()
411 ptrStringType = reflect.TypeFor[*string]()
412 countType = reflect.TypeFor[CountFlag]()
413 funcType = reflect.TypeFor[func(string)]()
414 )
415
416 v := reflect.ValueOf(&Flag).Elem()
417 t := v.Type()
418 for i := 0; i < t.NumField(); i++ {
419 f := t.Field(i)
420 if f.Name == "Cfg" {
421 continue
422 }
423
424 var name string
425 if len(f.Name) == 1 {
426 name = f.Name
427 } else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
428 name = string(rune(f.Name[5] + 'a' - 'A'))
429 } else {
430 name = strings.ToLower(f.Name)
431 }
432 if tag := f.Tag.Get("flag"); tag != "" {
433 name = tag
434 }
435
436 help := f.Tag.Get("help")
437 if help == "" {
438 panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
439 }
440
441 if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
442 panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
443 }
444
445 switch f.Type {
446 case boolType:
447 p := v.Field(i).Addr().Interface().(*bool)
448 flag.BoolVar(p, name, *p, help)
449 case intType:
450 p := v.Field(i).Addr().Interface().(*int)
451 flag.IntVar(p, name, *p, help)
452 case stringType:
453 p := v.Field(i).Addr().Interface().(*string)
454 flag.StringVar(p, name, *p, help)
455 case ptrBoolType:
456 p := v.Field(i).Interface().(*bool)
457 flag.BoolVar(p, name, *p, help)
458 case ptrIntType:
459 p := v.Field(i).Interface().(*int)
460 flag.IntVar(p, name, *p, help)
461 case ptrStringType:
462 p := v.Field(i).Interface().(*string)
463 flag.StringVar(p, name, *p, help)
464 case countType:
465 p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
466 objabi.Flagcount(name, help, p)
467 case funcType:
468 f := v.Field(i).Interface().(func(string))
469 objabi.Flagfn1(name, help, f)
470 default:
471 if val, ok := v.Field(i).Interface().(flag.Value); ok {
472 flag.Var(val, name, help)
473 } else {
474 panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
475 }
476 }
477 }
478 }
479
480
481
482 func concurrentFlagOk() bool {
483
484 return Flag.Percent == 0 &&
485 Flag.E == 0 &&
486 Flag.K == 0 &&
487 Flag.L == 0 &&
488 Flag.LowerJ == 0 &&
489 Flag.LowerM == 0 &&
490 Flag.LowerR == 0
491 }
492
493 func concurrentBackendAllowed() bool {
494 if !concurrentFlagOk() {
495 return false
496 }
497
498
499
500
501
502 if Ctxt.Debugvlog || !Debug.ConcurrentOk || Flag.Live > 0 {
503 return false
504 }
505
506 if buildcfg.Experiment.FieldTrack {
507 return false
508 }
509
510 if Ctxt.Flag_dynlink || Flag.Race {
511 return false
512 }
513 return true
514 }
515
516 func addImportDir(dir string) {
517 if dir != "" {
518 Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
519 }
520 }
521
522 func readImportCfg(file string) {
523 if Flag.Cfg.ImportMap == nil {
524 Flag.Cfg.ImportMap = make(map[string]string)
525 }
526 Flag.Cfg.PackageFile = map[string]string{}
527 data, err := os.ReadFile(file)
528 if err != nil {
529 log.Fatalf("-importcfg: %v", err)
530 }
531
532 for lineNum, line := range strings.Split(string(data), "\n") {
533 lineNum++
534 line = strings.TrimSpace(line)
535 if line == "" || strings.HasPrefix(line, "#") {
536 continue
537 }
538
539 verb, args, found := strings.Cut(line, " ")
540 if found {
541 args = strings.TrimSpace(args)
542 }
543 before, after, hasEq := strings.Cut(args, "=")
544
545 switch verb {
546 default:
547 log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
548 case "importmap":
549 if !hasEq || before == "" || after == "" {
550 log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
551 }
552 Flag.Cfg.ImportMap[before] = after
553 case "packagefile":
554 if !hasEq || before == "" || after == "" {
555 log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
556 }
557 Flag.Cfg.PackageFile[before] = after
558 }
559 }
560 }
561
562 func readCoverageCfg(file string) {
563 var cfg covcmd.CoverFixupConfig
564 data, err := os.ReadFile(file)
565 if err != nil {
566 log.Fatalf("-coveragecfg: %v", err)
567 }
568 if err := json.Unmarshal(data, &cfg); err != nil {
569 log.Fatalf("error reading -coveragecfg file %q: %v", file, err)
570 }
571 Flag.Cfg.CoverageInfo = &cfg
572 }
573
574 func readEmbedCfg(file string) {
575 data, err := os.ReadFile(file)
576 if err != nil {
577 log.Fatalf("-embedcfg: %v", err)
578 }
579 if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
580 log.Fatalf("%s: %v", file, err)
581 }
582 if Flag.Cfg.Embed.Patterns == nil {
583 log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
584 }
585 if Flag.Cfg.Embed.Files == nil {
586 log.Fatalf("%s: invalid embedcfg: missing Files", file)
587 }
588 }
589
590
591 func parseSpectre(s string) {
592 for f := range strings.SplitSeq(s, ",") {
593 f = strings.TrimSpace(f)
594 switch f {
595 default:
596 log.Fatalf("unknown setting -spectre=%s", f)
597 case "":
598
599 case "all":
600 Flag.Cfg.SpectreIndex = true
601 Ctxt.Retpoline = true
602 case "index":
603 Flag.Cfg.SpectreIndex = true
604 case "ret":
605 Ctxt.Retpoline = true
606 }
607 }
608
609 if Flag.Cfg.SpectreIndex {
610 switch buildcfg.GOARCH {
611 case "amd64":
612
613 default:
614 log.Fatalf("GOARCH=%s does not support -spectre=index", buildcfg.GOARCH)
615 }
616 }
617 }
618
View as plain text