Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/compile/internal/base/flag.go

Documentation: cmd/compile/internal/base

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

View as plain text