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