]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/base/flag.go
[dev.typeparams] all: merge dev.regabi (ec741b0) into dev.typeparams
[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         ABIWrap            bool         "help:\"enable generation of ABI wrappers\""
86         ABIWrapLimit       int          "help:\"emit at most N ABI wrappers (for debugging)\""
87         AsmHdr             string       "help:\"write assembly header to `file`\""
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         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         }
135 }
136
137 // ParseFlags parses the command-line flags into Flag.
138 func ParseFlags() {
139         Flag.I = addImportDir
140
141         Flag.LowerC = 1
142         Flag.LowerD = parseDebug
143         Flag.LowerP = &Ctxt.Pkgpath
144         Flag.LowerV = &Ctxt.Debugvlog
145
146         Flag.ABIWrap = objabi.Regabi_enabled != 0
147         Flag.Dwarf = objabi.GOARCH != "wasm"
148         Flag.DwarfBASEntries = &Ctxt.UseBASEntries
149         Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
150         *Flag.DwarfLocationLists = true
151         Flag.Dynlink = &Ctxt.Flag_dynlink
152         Flag.EmbedCfg = readEmbedCfg
153         Flag.GenDwarfInl = 2
154         Flag.ImportCfg = readImportCfg
155         Flag.ImportMap = addImportMap
156         Flag.LinkShared = &Ctxt.Flag_linkshared
157         Flag.Shared = &Ctxt.Flag_shared
158         Flag.WB = true
159
160         Flag.Cfg.ImportMap = make(map[string]string)
161
162         objabi.AddVersionFlag() // -V
163         registerFlags()
164         objabi.Flagparse(usage)
165
166         if Flag.MSan && !sys.MSanSupported(objabi.GOOS, objabi.GOARCH) {
167                 log.Fatalf("%s/%s does not support -msan", objabi.GOOS, objabi.GOARCH)
168         }
169         if Flag.Race && !sys.RaceDetectorSupported(objabi.GOOS, objabi.GOARCH) {
170                 log.Fatalf("%s/%s does not support -race", objabi.GOOS, objabi.GOARCH)
171         }
172         if (*Flag.Shared || *Flag.Dynlink || *Flag.LinkShared) && !Ctxt.Arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) {
173                 log.Fatalf("%s/%s does not support -shared", objabi.GOOS, objabi.GOARCH)
174         }
175         parseSpectre(Flag.Spectre) // left as string for recordFlags
176
177         Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
178         Ctxt.Flag_optimize = Flag.N == 0
179         Ctxt.Debugasm = int(Flag.S)
180
181         if flag.NArg() < 1 {
182                 usage()
183         }
184
185         if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
186                 fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
187                 Exit(2)
188         }
189
190         if Flag.LowerO == "" {
191                 p := flag.Arg(0)
192                 if i := strings.LastIndex(p, "/"); i >= 0 {
193                         p = p[i+1:]
194                 }
195                 if runtime.GOOS == "windows" {
196                         if i := strings.LastIndex(p, `\`); i >= 0 {
197                                 p = p[i+1:]
198                         }
199                 }
200                 if i := strings.LastIndex(p, "."); i >= 0 {
201                         p = p[:i]
202                 }
203                 suffix := ".o"
204                 if Flag.Pack {
205                         suffix = ".a"
206                 }
207                 Flag.LowerO = p + suffix
208         }
209
210         if Flag.Race && Flag.MSan {
211                 log.Fatal("cannot use both -race and -msan")
212         }
213         if Flag.Race || Flag.MSan {
214                 // -race and -msan imply -d=checkptr for now.
215                 Debug.Checkptr = 1
216         }
217
218         if Flag.CompilingRuntime && Flag.N != 0 {
219                 log.Fatal("cannot disable optimizations while compiling runtime")
220         }
221         if Flag.LowerC < 1 {
222                 log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
223         }
224         if Flag.LowerC > 1 && !concurrentBackendAllowed() {
225                 log.Fatalf("cannot use concurrent backend compilation with provided flags; invoked as %v", os.Args)
226         }
227
228         if Flag.CompilingRuntime {
229                 // Runtime can't use -d=checkptr, at least not yet.
230                 Debug.Checkptr = 0
231
232                 // Fuzzing the runtime isn't interesting either.
233                 Debug.Libfuzzer = 0
234         }
235
236         // set via a -d flag
237         Ctxt.Debugpcln = Debug.PCTab
238 }
239
240 // registerFlags adds flag registrations for all the fields in Flag.
241 // See the comment on type CmdFlags for the rules.
242 func registerFlags() {
243         var (
244                 boolType      = reflect.TypeOf(bool(false))
245                 intType       = reflect.TypeOf(int(0))
246                 stringType    = reflect.TypeOf(string(""))
247                 ptrBoolType   = reflect.TypeOf(new(bool))
248                 ptrIntType    = reflect.TypeOf(new(int))
249                 ptrStringType = reflect.TypeOf(new(string))
250                 countType     = reflect.TypeOf(CountFlag(0))
251                 funcType      = reflect.TypeOf((func(string))(nil))
252         )
253
254         v := reflect.ValueOf(&Flag).Elem()
255         t := v.Type()
256         for i := 0; i < t.NumField(); i++ {
257                 f := t.Field(i)
258                 if f.Name == "Cfg" {
259                         continue
260                 }
261
262                 var name string
263                 if len(f.Name) == 1 {
264                         name = f.Name
265                 } else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
266                         name = string(rune(f.Name[5] + 'a' - 'A'))
267                 } else {
268                         name = strings.ToLower(f.Name)
269                 }
270                 if tag := f.Tag.Get("flag"); tag != "" {
271                         name = tag
272                 }
273
274                 help := f.Tag.Get("help")
275                 if help == "" {
276                         panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
277                 }
278
279                 if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
280                         panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
281                 }
282
283                 switch f.Type {
284                 case boolType:
285                         p := v.Field(i).Addr().Interface().(*bool)
286                         flag.BoolVar(p, name, *p, help)
287                 case intType:
288                         p := v.Field(i).Addr().Interface().(*int)
289                         flag.IntVar(p, name, *p, help)
290                 case stringType:
291                         p := v.Field(i).Addr().Interface().(*string)
292                         flag.StringVar(p, name, *p, help)
293                 case ptrBoolType:
294                         p := v.Field(i).Interface().(*bool)
295                         flag.BoolVar(p, name, *p, help)
296                 case ptrIntType:
297                         p := v.Field(i).Interface().(*int)
298                         flag.IntVar(p, name, *p, help)
299                 case ptrStringType:
300                         p := v.Field(i).Interface().(*string)
301                         flag.StringVar(p, name, *p, help)
302                 case countType:
303                         p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
304                         objabi.Flagcount(name, help, p)
305                 case funcType:
306                         f := v.Field(i).Interface().(func(string))
307                         objabi.Flagfn1(name, help, f)
308                 }
309         }
310 }
311
312 // concurrentFlagOk reports whether the current compiler flags
313 // are compatible with concurrent compilation.
314 func concurrentFlagOk() bool {
315         // TODO(rsc): Many of these are fine. Remove them.
316         return Flag.Percent == 0 &&
317                 Flag.E == 0 &&
318                 Flag.K == 0 &&
319                 Flag.L == 0 &&
320                 Flag.LowerH == 0 &&
321                 Flag.LowerJ == 0 &&
322                 Flag.LowerM == 0 &&
323                 Flag.LowerR == 0
324 }
325
326 func concurrentBackendAllowed() bool {
327         if !concurrentFlagOk() {
328                 return false
329         }
330
331         // Debug.S by itself is ok, because all printing occurs
332         // while writing the object file, and that is non-concurrent.
333         // Adding Debug_vlog, however, causes Debug.S to also print
334         // while flushing the plist, which happens concurrently.
335         if Ctxt.Debugvlog || Debug.Any() || Flag.Live > 0 {
336                 return false
337         }
338         // TODO: Test and delete this condition.
339         if objabi.Fieldtrack_enabled != 0 {
340                 return false
341         }
342         // TODO: fix races and enable the following flags
343         if Ctxt.Flag_shared || Ctxt.Flag_dynlink || Flag.Race {
344                 return false
345         }
346         return true
347 }
348
349 func addImportDir(dir string) {
350         if dir != "" {
351                 Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
352         }
353 }
354
355 func addImportMap(s string) {
356         if Flag.Cfg.ImportMap == nil {
357                 Flag.Cfg.ImportMap = make(map[string]string)
358         }
359         if strings.Count(s, "=") != 1 {
360                 log.Fatal("-importmap argument must be of the form source=actual")
361         }
362         i := strings.Index(s, "=")
363         source, actual := s[:i], s[i+1:]
364         if source == "" || actual == "" {
365                 log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty")
366         }
367         Flag.Cfg.ImportMap[source] = actual
368 }
369
370 func readImportCfg(file string) {
371         if Flag.Cfg.ImportMap == nil {
372                 Flag.Cfg.ImportMap = make(map[string]string)
373         }
374         Flag.Cfg.PackageFile = map[string]string{}
375         data, err := ioutil.ReadFile(file)
376         if err != nil {
377                 log.Fatalf("-importcfg: %v", err)
378         }
379
380         for lineNum, line := range strings.Split(string(data), "\n") {
381                 lineNum++ // 1-based
382                 line = strings.TrimSpace(line)
383                 if line == "" || strings.HasPrefix(line, "#") {
384                         continue
385                 }
386
387                 var verb, args string
388                 if i := strings.Index(line, " "); i < 0 {
389                         verb = line
390                 } else {
391                         verb, args = line[:i], strings.TrimSpace(line[i+1:])
392                 }
393                 var before, after string
394                 if i := strings.Index(args, "="); i >= 0 {
395                         before, after = args[:i], args[i+1:]
396                 }
397                 switch verb {
398                 default:
399                         log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
400                 case "importmap":
401                         if before == "" || after == "" {
402                                 log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
403                         }
404                         Flag.Cfg.ImportMap[before] = after
405                 case "packagefile":
406                         if before == "" || after == "" {
407                                 log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
408                         }
409                         Flag.Cfg.PackageFile[before] = after
410                 }
411         }
412 }
413
414 func readEmbedCfg(file string) {
415         data, err := ioutil.ReadFile(file)
416         if err != nil {
417                 log.Fatalf("-embedcfg: %v", err)
418         }
419         if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
420                 log.Fatalf("%s: %v", file, err)
421         }
422         if Flag.Cfg.Embed.Patterns == nil {
423                 log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
424         }
425         if Flag.Cfg.Embed.Files == nil {
426                 log.Fatalf("%s: invalid embedcfg: missing Files", file)
427         }
428 }
429
430 // parseSpectre parses the spectre configuration from the string s.
431 func parseSpectre(s string) {
432         for _, f := range strings.Split(s, ",") {
433                 f = strings.TrimSpace(f)
434                 switch f {
435                 default:
436                         log.Fatalf("unknown setting -spectre=%s", f)
437                 case "":
438                         // nothing
439                 case "all":
440                         Flag.Cfg.SpectreIndex = true
441                         Ctxt.Retpoline = true
442                 case "index":
443                         Flag.Cfg.SpectreIndex = true
444                 case "ret":
445                         Ctxt.Retpoline = true
446                 }
447         }
448
449         if Flag.Cfg.SpectreIndex {
450                 switch objabi.GOARCH {
451                 case "amd64":
452                         // ok
453                 default:
454                         log.Fatalf("GOARCH=%s does not support -spectre=index", objabi.GOARCH)
455                 }
456         }
457 }