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