1
2
3
4
5 package base
6
7 import (
8 "cmd/internal/cov/covcmd"
9 "encoding/json"
10 "flag"
11 "fmt"
12 "internal/buildcfg"
13 "internal/platform"
14 "log"
15 "os"
16 "reflect"
17 "runtime"
18 "strings"
19
20 "cmd/internal/obj"
21 "cmd/internal/objabi"
22 "cmd/internal/sys"
23 )
24
25 func usage() {
26 fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
27 objabi.Flagprint(os.Stderr)
28 Exit(2)
29 }
30
31
32
33 var Flag CmdFlags
34
35
36
37
38 type CountFlag int
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 type CmdFlags struct {
55
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
66 W CountFlag "help:\"debug parse tree after type checking\""
67
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`\""
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\""
81
82
83 Percent CountFlag "flag:\"%\" help:\"debug non-static initializers\""
84 CompilingRuntime bool "flag:\"+\" help:\"compiling runtime\""
85
86
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\""
98 DwarfLocationLists *bool "help:\"add location lists to DWARF in optimized mode\""
99 Dynlink *bool "help:\"support references to Go symbols defined in other shared libraries\""
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\""
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\""
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\""
120 SmallFrames bool "help:\"reduce the size limit for stack allocated objects\""
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\""
127 PgoProfile string "help:\"read profile from `file`\""
128 ErrorURL bool "help:\"print explanatory URL with error message if applicable\""
129
130
131 Cfg struct {
132 Embed struct {
133 Patterns map[string][]string
134 Files map[string]string
135 }
136 ImportDirs []string
137 ImportMap map[string]string
138 PackageFile map[string]string
139 CoverageInfo *covcmd.CoverFixupConfig
140 SpectreIndex bool
141
142
143 Instrumenting bool
144 }
145 }
146
147 func addEnv(s string) {
148 i := strings.Index(s, "=")
149 if i < 0 {
150 log.Fatal("-env argument must be of the form key=value")
151 }
152 os.Setenv(s[:i], s[i+1:])
153 }
154
155
156 func ParseFlags() {
157 Flag.I = addImportDir
158
159 Flag.LowerC = runtime.GOMAXPROCS(0)
160 Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA)
161 Flag.LowerP = &Ctxt.Pkgpath
162 Flag.LowerV = &Ctxt.Debugvlog
163
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
170 Flag.Env = addEnv
171 Flag.GenDwarfInl = 2
172 Flag.ImportCfg = readImportCfg
173 Flag.CoverageCfg = readCoverageCfg
174 Flag.LinkShared = &Ctxt.Flag_linkshared
175 Flag.Shared = &Ctxt.Flag_shared
176 Flag.WB = true
177
178 Debug.ConcurrentOk = true
179 Debug.MaxShapeLen = 500
180 Debug.InlFuncsWithClosures = 1
181 Debug.InlStaticInit = 1
182 Debug.PGOInline = 1
183 Debug.PGODevirtualize = 2
184 Debug.SyncFrames = -1
185 Debug.ZeroCopy = 1
186 Debug.RangeFuncCheck = 1
187
188 Debug.Checkptr = -1
189
190 Flag.Cfg.ImportMap = make(map[string]string)
191
192 objabi.AddVersionFlag()
193 registerFlags()
194 objabi.Flagparse(usage)
195
196 if gcd := os.Getenv("GOCOMPILEDEBUG"); gcd != "" {
197
198
199 Flag.LowerD.Set(gcd)
200 }
201
202 if Debug.Gossahash != "" {
203 hashDebug = NewHashDebug("gossahash", Debug.Gossahash, nil)
204 }
205
206
207
208 if Flag.Std && objabi.LookupPkgSpecial(Ctxt.Pkgpath).Runtime {
209 Flag.CompilingRuntime = true
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235 if Debug.LoopVarHash != "" {
236
237 mostInlineOnly := true
238 if strings.HasPrefix(Debug.LoopVarHash, "IL") {
239
240
241
242
243
244 Debug.LoopVarHash = Debug.LoopVarHash[2:]
245 mostInlineOnly = false
246 }
247
248 LoopVarHash = NewHashDebug("loopvarhash", Debug.LoopVarHash, nil)
249 if Debug.LoopVar < 11 {
250 Debug.LoopVar = 1
251 }
252 LoopVarHash.SetInlineSuffixOnly(mostInlineOnly)
253 } else if buildcfg.Experiment.LoopVar && Debug.LoopVar == 0 {
254 Debug.LoopVar = 1
255 }
256
257 if Debug.Fmahash != "" {
258 FmaHash = NewHashDebug("fmahash", Debug.Fmahash, nil)
259 }
260 if Debug.PGOHash != "" {
261 PGOHash = NewHashDebug("pgohash", Debug.PGOHash, nil)
262 }
263
264 if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
265 log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
266 }
267 if Flag.ASan && !platform.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
268 log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
269 }
270 if Flag.Race && !platform.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
271 log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
272 }
273 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) {
274 log.Fatalf("%s/%s does not support -shared", buildcfg.GOOS, buildcfg.GOARCH)
275 }
276 parseSpectre(Flag.Spectre)
277
278 Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
279 Ctxt.Flag_optimize = Flag.N == 0
280 Ctxt.Debugasm = int(Flag.S)
281 Ctxt.Flag_maymorestack = Debug.MayMoreStack
282 Ctxt.Flag_noRefName = Debug.NoRefName != 0
283
284 if flag.NArg() < 1 {
285 usage()
286 }
287
288 if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
289 fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
290 Exit(2)
291 }
292
293 if *Flag.LowerP == "" {
294 *Flag.LowerP = obj.UnlinkablePkg
295 }
296
297 if Flag.LowerO == "" {
298 p := flag.Arg(0)
299 if i := strings.LastIndex(p, "/"); i >= 0 {
300 p = p[i+1:]
301 }
302 if runtime.GOOS == "windows" {
303 if i := strings.LastIndex(p, `\`); i >= 0 {
304 p = p[i+1:]
305 }
306 }
307 if i := strings.LastIndex(p, "."); i >= 0 {
308 p = p[:i]
309 }
310 suffix := ".o"
311 if Flag.Pack {
312 suffix = ".a"
313 }
314 Flag.LowerO = p + suffix
315 }
316 switch {
317 case Flag.Race && Flag.MSan:
318 log.Fatal("cannot use both -race and -msan")
319 case Flag.Race && Flag.ASan:
320 log.Fatal("cannot use both -race and -asan")
321 case Flag.MSan && Flag.ASan:
322 log.Fatal("cannot use both -msan and -asan")
323 }
324 if Flag.Race || Flag.MSan || Flag.ASan {
325
326 if Debug.Checkptr == -1 {
327 Debug.Checkptr = 1
328 }
329 }
330
331 if Flag.LowerC < 1 {
332 log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
333 }
334 if !concurrentBackendAllowed() {
335 Flag.LowerC = 1
336 }
337
338 if Flag.CompilingRuntime {
339
340
341 Flag.N = 0
342 Ctxt.Flag_optimize = true
343
344
345 Debug.Checkptr = 0
346
347
348 Debug.Libfuzzer = 0
349 }
350
351 if Debug.Checkptr == -1 {
352 Debug.Checkptr = 0
353 }
354
355
356 Ctxt.Debugpcln = Debug.PCTab
357 }
358
359
360
361 func registerFlags() {
362 var (
363 boolType = reflect.TypeOf(bool(false))
364 intType = reflect.TypeOf(int(0))
365 stringType = reflect.TypeOf(string(""))
366 ptrBoolType = reflect.TypeOf(new(bool))
367 ptrIntType = reflect.TypeOf(new(int))
368 ptrStringType = reflect.TypeOf(new(string))
369 countType = reflect.TypeOf(CountFlag(0))
370 funcType = reflect.TypeOf((func(string))(nil))
371 )
372
373 v := reflect.ValueOf(&Flag).Elem()
374 t := v.Type()
375 for i := 0; i < t.NumField(); i++ {
376 f := t.Field(i)
377 if f.Name == "Cfg" {
378 continue
379 }
380
381 var name string
382 if len(f.Name) == 1 {
383 name = f.Name
384 } else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
385 name = string(rune(f.Name[5] + 'a' - 'A'))
386 } else {
387 name = strings.ToLower(f.Name)
388 }
389 if tag := f.Tag.Get("flag"); tag != "" {
390 name = tag
391 }
392
393 help := f.Tag.Get("help")
394 if help == "" {
395 panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
396 }
397
398 if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
399 panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
400 }
401
402 switch f.Type {
403 case boolType:
404 p := v.Field(i).Addr().Interface().(*bool)
405 flag.BoolVar(p, name, *p, help)
406 case intType:
407 p := v.Field(i).Addr().Interface().(*int)
408 flag.IntVar(p, name, *p, help)
409 case stringType:
410 p := v.Field(i).Addr().Interface().(*string)
411 flag.StringVar(p, name, *p, help)
412 case ptrBoolType:
413 p := v.Field(i).Interface().(*bool)
414 flag.BoolVar(p, name, *p, help)
415 case ptrIntType:
416 p := v.Field(i).Interface().(*int)
417 flag.IntVar(p, name, *p, help)
418 case ptrStringType:
419 p := v.Field(i).Interface().(*string)
420 flag.StringVar(p, name, *p, help)
421 case countType:
422 p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
423 objabi.Flagcount(name, help, p)
424 case funcType:
425 f := v.Field(i).Interface().(func(string))
426 objabi.Flagfn1(name, help, f)
427 default:
428 if val, ok := v.Field(i).Interface().(flag.Value); ok {
429 flag.Var(val, name, help)
430 } else {
431 panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
432 }
433 }
434 }
435 }
436
437
438
439 func concurrentFlagOk() bool {
440
441 return Flag.Percent == 0 &&
442 Flag.E == 0 &&
443 Flag.K == 0 &&
444 Flag.L == 0 &&
445 Flag.LowerH == 0 &&
446 Flag.LowerJ == 0 &&
447 Flag.LowerM == 0 &&
448 Flag.LowerR == 0
449 }
450
451 func concurrentBackendAllowed() bool {
452 if !concurrentFlagOk() {
453 return false
454 }
455
456
457
458
459
460 if Ctxt.Debugvlog || !Debug.ConcurrentOk || Flag.Live > 0 {
461 return false
462 }
463
464 if buildcfg.Experiment.FieldTrack {
465 return false
466 }
467
468 if Ctxt.Flag_dynlink || Flag.Race {
469 return false
470 }
471 return true
472 }
473
474 func addImportDir(dir string) {
475 if dir != "" {
476 Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
477 }
478 }
479
480 func readImportCfg(file string) {
481 if Flag.Cfg.ImportMap == nil {
482 Flag.Cfg.ImportMap = make(map[string]string)
483 }
484 Flag.Cfg.PackageFile = map[string]string{}
485 data, err := os.ReadFile(file)
486 if err != nil {
487 log.Fatalf("-importcfg: %v", err)
488 }
489
490 for lineNum, line := range strings.Split(string(data), "\n") {
491 lineNum++
492 line = strings.TrimSpace(line)
493 if line == "" || strings.HasPrefix(line, "#") {
494 continue
495 }
496
497 verb, args, found := strings.Cut(line, " ")
498 if found {
499 args = strings.TrimSpace(args)
500 }
501 before, after, hasEq := strings.Cut(args, "=")
502
503 switch verb {
504 default:
505 log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
506 case "importmap":
507 if !hasEq || before == "" || after == "" {
508 log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
509 }
510 Flag.Cfg.ImportMap[before] = after
511 case "packagefile":
512 if !hasEq || before == "" || after == "" {
513 log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
514 }
515 Flag.Cfg.PackageFile[before] = after
516 }
517 }
518 }
519
520 func readCoverageCfg(file string) {
521 var cfg covcmd.CoverFixupConfig
522 data, err := os.ReadFile(file)
523 if err != nil {
524 log.Fatalf("-coveragecfg: %v", err)
525 }
526 if err := json.Unmarshal(data, &cfg); err != nil {
527 log.Fatalf("error reading -coveragecfg file %q: %v", file, err)
528 }
529 Flag.Cfg.CoverageInfo = &cfg
530 }
531
532 func readEmbedCfg(file string) {
533 data, err := os.ReadFile(file)
534 if err != nil {
535 log.Fatalf("-embedcfg: %v", err)
536 }
537 if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
538 log.Fatalf("%s: %v", file, err)
539 }
540 if Flag.Cfg.Embed.Patterns == nil {
541 log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
542 }
543 if Flag.Cfg.Embed.Files == nil {
544 log.Fatalf("%s: invalid embedcfg: missing Files", file)
545 }
546 }
547
548
549 func parseSpectre(s string) {
550 for _, f := range strings.Split(s, ",") {
551 f = strings.TrimSpace(f)
552 switch f {
553 default:
554 log.Fatalf("unknown setting -spectre=%s", f)
555 case "":
556
557 case "all":
558 Flag.Cfg.SpectreIndex = true
559 Ctxt.Retpoline = true
560 case "index":
561 Flag.Cfg.SpectreIndex = true
562 case "ret":
563 Ctxt.Retpoline = true
564 }
565 }
566
567 if Flag.Cfg.SpectreIndex {
568 switch buildcfg.GOARCH {
569 case "amd64":
570
571 default:
572 log.Fatalf("GOARCH=%s does not support -spectre=index", buildcfg.GOARCH)
573 }
574 }
575 }
576
View as plain text