1
2
3
4
5 package base
6
7 import (
8 "encoding/json"
9 "flag"
10 "fmt"
11 "internal/buildcfg"
12 "io/ioutil"
13 "log"
14 "os"
15 "reflect"
16 "runtime"
17 "strings"
18
19 "cmd/internal/objabi"
20 "cmd/internal/sys"
21 )
22
23 func usage() {
24 fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
25 objabi.Flagprint(os.Stderr)
26 Exit(2)
27 }
28
29
30
31 var Flag CmdFlags
32
33
34
35
36 type CountFlag int
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 type CmdFlags struct {
53
54 B CountFlag "help:\"disable bounds checking\""
55 C CountFlag "help:\"disable printing of columns in error messages\""
56 D string "help:\"set relative `path` for local imports\""
57 E CountFlag "help:\"debug symbol export\""
58 G CountFlag "help:\"accept generic code\""
59 I func(string) "help:\"add `directory` to import search path\""
60 K CountFlag "help:\"debug missing line numbers\""
61 L CountFlag "help:\"show full file names in error messages\""
62 N CountFlag "help:\"disable optimizations\""
63 S CountFlag "help:\"print assembly listing\""
64
65 W CountFlag "help:\"debug parse tree after type checking\""
66
67 LowerC int "help:\"concurrency during compilation (1 means no concurrency)\""
68 LowerD flag.Value "help:\"enable debugging settings; try -d help\""
69 LowerE CountFlag "help:\"no limit on number of errors reported\""
70 LowerH CountFlag "help:\"halt on error\""
71 LowerJ CountFlag "help:\"debug runtime-initialized variables\""
72 LowerL CountFlag "help:\"disable inlining\""
73 LowerM CountFlag "help:\"print optimization decisions\""
74 LowerO string "help:\"write output to `file`\""
75 LowerP *string "help:\"set expected package import `path`\""
76 LowerR CountFlag "help:\"debug generated wrappers\""
77 LowerT bool "help:\"enable tracing for debugging the compiler\""
78 LowerW CountFlag "help:\"debug type checking\""
79 LowerV *bool "help:\"increase debug verbosity\""
80
81
82 Percent int "flag:\"%\" help:\"debug non-static initializers\""
83 CompilingRuntime bool "flag:\"+\" help:\"compiling runtime\""
84
85
86 AsmHdr string "help:\"write assembly header to `file`\""
87 ASan bool "help:\"build code compatible with C/C++ address sanitizer\""
88 Bench string "help:\"append benchmark times to `file`\""
89 BlockProfile string "help:\"write block profile to `file`\""
90 BuildID string "help:\"record `id` as the build id in the export metadata\""
91 CPUProfile string "help:\"write cpu profile to `file`\""
92 Complete bool "help:\"compiling complete package (no C or assembly)\""
93 ClobberDead bool "help:\"clobber dead stack slots (for debugging)\""
94 ClobberDeadReg bool "help:\"clobber dead registers (for debugging)\""
95 Dwarf bool "help:\"generate DWARF symbols\""
96 DwarfBASEntries *bool "help:\"use base address selection entries in DWARF\""
97 DwarfLocationLists *bool "help:\"add location lists to DWARF in optimized mode\""
98 Dynlink *bool "help:\"support references to Go symbols defined in other shared libraries\""
99 EmbedCfg func(string) "help:\"read go:embed configuration from `file`\""
100 GenDwarfInl int "help:\"generate DWARF inline info records\""
101 GoVersion string "help:\"required version of the runtime\""
102 ImportCfg func(string) "help:\"read import configuration from `file`\""
103 ImportMap func(string) "help:\"add `definition` of the form source=actual to import map\""
104 InstallSuffix string "help:\"set pkg directory `suffix`\""
105 JSON string "help:\"version,file for JSON compiler/optimizer detail output\""
106 Lang string "help:\"Go language version source code expects\""
107 LinkObj string "help:\"write linker-specific object to `file`\""
108 LinkShared *bool "help:\"generate code that will be linked against Go shared libraries\""
109 Live CountFlag "help:\"debug liveness analysis\""
110 MSan bool "help:\"build code compatible with C/C++ memory sanitizer\""
111 MemProfile string "help:\"write memory profile to `file`\""
112 MemProfileRate int "help:\"set runtime.MemProfileRate to `rate`\""
113 MutexProfile string "help:\"write mutex profile to `file`\""
114 NoLocalImports bool "help:\"reject local (relative) imports\""
115 Pack bool "help:\"write to file.a instead of file.o\""
116 Race bool "help:\"enable race detector\""
117 Shared *bool "help:\"generate code that can be linked into a shared library\""
118 SmallFrames bool "help:\"reduce the size limit for stack allocated objects\""
119 Spectre string "help:\"enable spectre mitigations in `list` (all, index, ret)\""
120 Std bool "help:\"compiling standard library\""
121 SymABIs string "help:\"read symbol ABIs from `file`\""
122 TraceProfile string "help:\"write an execution trace to `file`\""
123 TrimPath string "help:\"remove `prefix` from recorded source file paths\""
124 WB bool "help:\"enable write barrier\""
125
126
127 Cfg struct {
128 Embed struct {
129 Patterns map[string][]string
130 Files map[string]string
131 }
132 ImportDirs []string
133 ImportMap map[string]string
134 PackageFile map[string]string
135 SpectreIndex bool
136
137
138 Instrumenting bool
139 }
140 }
141
142
143 func ParseFlags() {
144 Flag.G = 3
145 Flag.I = addImportDir
146
147 Flag.LowerC = 1
148 Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA)
149 Flag.LowerP = &Ctxt.Pkgpath
150 Flag.LowerV = &Ctxt.Debugvlog
151
152 Flag.Dwarf = buildcfg.GOARCH != "wasm"
153 Flag.DwarfBASEntries = &Ctxt.UseBASEntries
154 Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
155 *Flag.DwarfLocationLists = true
156 Flag.Dynlink = &Ctxt.Flag_dynlink
157 Flag.EmbedCfg = readEmbedCfg
158 Flag.GenDwarfInl = 2
159 Flag.ImportCfg = readImportCfg
160 Flag.ImportMap = addImportMap
161 Flag.LinkShared = &Ctxt.Flag_linkshared
162 Flag.Shared = &Ctxt.Flag_shared
163 Flag.WB = true
164
165 Debug.InlFuncsWithClosures = 1
166 if buildcfg.Experiment.Unified {
167 Debug.Unified = 1
168 }
169
170 Debug.Checkptr = -1
171
172 Flag.Cfg.ImportMap = make(map[string]string)
173
174 objabi.AddVersionFlag()
175 registerFlags()
176 objabi.Flagparse(usage)
177
178 if Flag.MSan && !sys.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
179 log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
180 }
181 if Flag.ASan && !sys.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
182 log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
183 }
184 if Flag.Race && !sys.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
185 log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
186 }
187 if (*Flag.Shared || *Flag.Dynlink || *Flag.LinkShared) && !Ctxt.Arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) {
188 log.Fatalf("%s/%s does not support -shared", buildcfg.GOOS, buildcfg.GOARCH)
189 }
190 parseSpectre(Flag.Spectre)
191
192 Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
193 Ctxt.Flag_optimize = Flag.N == 0
194 Ctxt.Debugasm = int(Flag.S)
195 Ctxt.Flag_maymorestack = Debug.MayMoreStack
196
197 if flag.NArg() < 1 {
198 usage()
199 }
200
201 if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
202 fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
203 Exit(2)
204 }
205
206 if Flag.LowerO == "" {
207 p := flag.Arg(0)
208 if i := strings.LastIndex(p, "/"); i >= 0 {
209 p = p[i+1:]
210 }
211 if runtime.GOOS == "windows" {
212 if i := strings.LastIndex(p, `\`); i >= 0 {
213 p = p[i+1:]
214 }
215 }
216 if i := strings.LastIndex(p, "."); i >= 0 {
217 p = p[:i]
218 }
219 suffix := ".o"
220 if Flag.Pack {
221 suffix = ".a"
222 }
223 Flag.LowerO = p + suffix
224 }
225 switch {
226 case Flag.Race && Flag.MSan:
227 log.Fatal("cannot use both -race and -msan")
228 case Flag.Race && Flag.ASan:
229 log.Fatal("cannot use both -race and -asan")
230 case Flag.MSan && Flag.ASan:
231 log.Fatal("cannot use both -msan and -asan")
232 }
233 if Flag.Race || Flag.MSan || Flag.ASan {
234
235 if Debug.Checkptr == -1 {
236 Debug.Checkptr = 1
237 }
238 }
239
240 if Flag.CompilingRuntime && Flag.N != 0 {
241 log.Fatal("cannot disable optimizations while compiling runtime")
242 }
243 if Flag.LowerC < 1 {
244 log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
245 }
246 if Flag.LowerC > 1 && !concurrentBackendAllowed() {
247 log.Fatalf("cannot use concurrent backend compilation with provided flags; invoked as %v", os.Args)
248 }
249
250 if Flag.CompilingRuntime {
251
252 Debug.Checkptr = 0
253
254
255 Debug.Libfuzzer = 0
256 }
257
258 if Debug.Checkptr == -1 {
259 Debug.Checkptr = 0
260 }
261
262
263 Ctxt.Debugpcln = Debug.PCTab
264 }
265
266
267
268 func registerFlags() {
269 var (
270 boolType = reflect.TypeOf(bool(false))
271 intType = reflect.TypeOf(int(0))
272 stringType = reflect.TypeOf(string(""))
273 ptrBoolType = reflect.TypeOf(new(bool))
274 ptrIntType = reflect.TypeOf(new(int))
275 ptrStringType = reflect.TypeOf(new(string))
276 countType = reflect.TypeOf(CountFlag(0))
277 funcType = reflect.TypeOf((func(string))(nil))
278 )
279
280 v := reflect.ValueOf(&Flag).Elem()
281 t := v.Type()
282 for i := 0; i < t.NumField(); i++ {
283 f := t.Field(i)
284 if f.Name == "Cfg" {
285 continue
286 }
287
288 var name string
289 if len(f.Name) == 1 {
290 name = f.Name
291 } else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
292 name = string(rune(f.Name[5] + 'a' - 'A'))
293 } else {
294 name = strings.ToLower(f.Name)
295 }
296 if tag := f.Tag.Get("flag"); tag != "" {
297 name = tag
298 }
299
300 help := f.Tag.Get("help")
301 if help == "" {
302 panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
303 }
304
305 if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
306 panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
307 }
308
309 switch f.Type {
310 case boolType:
311 p := v.Field(i).Addr().Interface().(*bool)
312 flag.BoolVar(p, name, *p, help)
313 case intType:
314 p := v.Field(i).Addr().Interface().(*int)
315 flag.IntVar(p, name, *p, help)
316 case stringType:
317 p := v.Field(i).Addr().Interface().(*string)
318 flag.StringVar(p, name, *p, help)
319 case ptrBoolType:
320 p := v.Field(i).Interface().(*bool)
321 flag.BoolVar(p, name, *p, help)
322 case ptrIntType:
323 p := v.Field(i).Interface().(*int)
324 flag.IntVar(p, name, *p, help)
325 case ptrStringType:
326 p := v.Field(i).Interface().(*string)
327 flag.StringVar(p, name, *p, help)
328 case countType:
329 p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
330 objabi.Flagcount(name, help, p)
331 case funcType:
332 f := v.Field(i).Interface().(func(string))
333 objabi.Flagfn1(name, help, f)
334 default:
335 if val, ok := v.Field(i).Interface().(flag.Value); ok {
336 flag.Var(val, name, help)
337 } else {
338 panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
339 }
340 }
341 }
342 }
343
344
345
346 func concurrentFlagOk() bool {
347
348 return Flag.Percent == 0 &&
349 Flag.E == 0 &&
350 Flag.K == 0 &&
351 Flag.L == 0 &&
352 Flag.LowerH == 0 &&
353 Flag.LowerJ == 0 &&
354 Flag.LowerM == 0 &&
355 Flag.LowerR == 0
356 }
357
358 func concurrentBackendAllowed() bool {
359 if !concurrentFlagOk() {
360 return false
361 }
362
363
364
365
366
367 if Ctxt.Debugvlog || Debug.Any || Flag.Live > 0 {
368 return false
369 }
370
371 if buildcfg.Experiment.FieldTrack {
372 return false
373 }
374
375 if Ctxt.Flag_dynlink || Flag.Race {
376 return false
377 }
378 return true
379 }
380
381 func addImportDir(dir string) {
382 if dir != "" {
383 Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
384 }
385 }
386
387 func addImportMap(s string) {
388 if Flag.Cfg.ImportMap == nil {
389 Flag.Cfg.ImportMap = make(map[string]string)
390 }
391 if strings.Count(s, "=") != 1 {
392 log.Fatal("-importmap argument must be of the form source=actual")
393 }
394 i := strings.Index(s, "=")
395 source, actual := s[:i], s[i+1:]
396 if source == "" || actual == "" {
397 log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty")
398 }
399 Flag.Cfg.ImportMap[source] = actual
400 }
401
402 func readImportCfg(file string) {
403 if Flag.Cfg.ImportMap == nil {
404 Flag.Cfg.ImportMap = make(map[string]string)
405 }
406 Flag.Cfg.PackageFile = map[string]string{}
407 data, err := ioutil.ReadFile(file)
408 if err != nil {
409 log.Fatalf("-importcfg: %v", err)
410 }
411
412 for lineNum, line := range strings.Split(string(data), "\n") {
413 lineNum++
414 line = strings.TrimSpace(line)
415 if line == "" || strings.HasPrefix(line, "#") {
416 continue
417 }
418
419 var verb, args string
420 if i := strings.Index(line, " "); i < 0 {
421 verb = line
422 } else {
423 verb, args = line[:i], strings.TrimSpace(line[i+1:])
424 }
425 var before, after string
426 if i := strings.Index(args, "="); i >= 0 {
427 before, after = args[:i], args[i+1:]
428 }
429 switch verb {
430 default:
431 log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
432 case "importmap":
433 if before == "" || after == "" {
434 log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
435 }
436 Flag.Cfg.ImportMap[before] = after
437 case "packagefile":
438 if before == "" || after == "" {
439 log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
440 }
441 Flag.Cfg.PackageFile[before] = after
442 }
443 }
444 }
445
446 func readEmbedCfg(file string) {
447 data, err := ioutil.ReadFile(file)
448 if err != nil {
449 log.Fatalf("-embedcfg: %v", err)
450 }
451 if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
452 log.Fatalf("%s: %v", file, err)
453 }
454 if Flag.Cfg.Embed.Patterns == nil {
455 log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
456 }
457 if Flag.Cfg.Embed.Files == nil {
458 log.Fatalf("%s: invalid embedcfg: missing Files", file)
459 }
460 }
461
462
463 func parseSpectre(s string) {
464 for _, f := range strings.Split(s, ",") {
465 f = strings.TrimSpace(f)
466 switch f {
467 default:
468 log.Fatalf("unknown setting -spectre=%s", f)
469 case "":
470
471 case "all":
472 Flag.Cfg.SpectreIndex = true
473 Ctxt.Retpoline = true
474 case "index":
475 Flag.Cfg.SpectreIndex = true
476 case "ret":
477 Ctxt.Retpoline = true
478 }
479 }
480
481 if Flag.Cfg.SpectreIndex {
482 switch buildcfg.GOARCH {
483 case "amd64":
484
485 default:
486 log.Fatalf("GOARCH=%s does not support -spectre=index", buildcfg.GOARCH)
487 }
488 }
489 }
490
View as plain text