Source file 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  	"cmd/internal/cov/covcmd"
     9  	"cmd/internal/telemetry/counter"
    10  	"encoding/json"
    11  	"flag"
    12  	"fmt"
    13  	"internal/buildcfg"
    14  	"internal/platform"
    15  	"log"
    16  	"os"
    17  	"reflect"
    18  	"runtime"
    19  	"strings"
    20  
    21  	"cmd/internal/obj"
    22  	"cmd/internal/objabi"
    23  	"cmd/internal/sys"
    24  )
    25  
    26  func usage() {
    27  	fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
    28  	objabi.Flagprint(os.Stderr)
    29  	Exit(2)
    30  }
    31  
    32  // Flag holds the parsed command-line flags.
    33  // See ParseFlag for non-zero defaults.
    34  var Flag CmdFlags
    35  
    36  // A CountFlag is a counting integer flag.
    37  // It accepts -name=value to set the value directly,
    38  // but it also accepts -name with no =value to increment the count.
    39  type CountFlag int
    40  
    41  // CmdFlags defines the command-line flags (see var Flag).
    42  // Each struct field is a different flag, by default named for the lower-case of the field name.
    43  // If the flag name is a single letter, the default flag name is left upper-case.
    44  // If the flag name is "Lower" followed by a single letter, the default flag name is the lower-case of the last letter.
    45  //
    46  // If this default flag name can't be made right, the `flag` struct tag can be used to replace it,
    47  // but this should be done only in exceptional circumstances: it helps everyone if the flag name
    48  // is obvious from the field name when the flag is used elsewhere in the compiler sources.
    49  // The `flag:"-"` struct tag makes a field invisible to the flag logic and should also be used sparingly.
    50  //
    51  // Each field must have a `help` struct tag giving the flag help message.
    52  //
    53  // The allowed field types are bool, int, string, pointers to those (for values stored elsewhere),
    54  // CountFlag (for a counting flag), and func(string) (for a flag that uses special code for parsing).
    55  type CmdFlags struct {
    56  	// Single letters
    57  	B CountFlag    "help:\"disable bounds checking\""
    58  	C CountFlag    "help:\"disable printing of columns in error messages\""
    59  	D string       "help:\"set relative `path` for local imports\""
    60  	E CountFlag    "help:\"debug symbol export\""
    61  	I func(string) "help:\"add `directory` to import search path\""
    62  	K CountFlag    "help:\"debug missing line numbers\""
    63  	L CountFlag    "help:\"also show actual source file names in error messages for positions affected by //line directives\""
    64  	N CountFlag    "help:\"disable optimizations\""
    65  	S CountFlag    "help:\"print assembly listing\""
    66  	// V is added by objabi.AddVersionFlag
    67  	W CountFlag "help:\"debug parse tree after type checking\""
    68  
    69  	LowerC int        "help:\"concurrency during compilation (1 means no concurrency)\""
    70  	LowerD flag.Value "help:\"enable debugging settings; try -d help\""
    71  	LowerE CountFlag  "help:\"no limit on number of errors reported\""
    72  	LowerH CountFlag  "help:\"halt on error\""
    73  	LowerJ CountFlag  "help:\"debug runtime-initialized variables\""
    74  	LowerL CountFlag  "help:\"disable inlining\""
    75  	LowerM CountFlag  "help:\"print optimization decisions\""
    76  	LowerO string     "help:\"write output to `file`\""
    77  	LowerP *string    "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below
    78  	LowerR CountFlag  "help:\"debug generated wrappers\""
    79  	LowerT bool       "help:\"enable tracing for debugging the compiler\""
    80  	LowerW CountFlag  "help:\"debug type checking\""
    81  	LowerU CountFlag  "help:\"emit unsorted warnings/errors\""
    82  	LowerV *bool      "help:\"increase debug verbosity\""
    83  
    84  	// Special characters
    85  	Percent          CountFlag "flag:\"%\" help:\"debug non-static initializers\""
    86  	CompilingRuntime bool      "flag:\"+\" help:\"compiling runtime\""
    87  
    88  	// Longer names
    89  	AsmHdr             string       "help:\"write assembly header to `file`\""
    90  	ASan               bool         "help:\"build code compatible with C/C++ address sanitizer\""
    91  	Bench              string       "help:\"append benchmark times to `file`\""
    92  	BlockProfile       string       "help:\"write block profile to `file`\""
    93  	BuildID            string       "help:\"record `id` as the build id in the export metadata\""
    94  	CPUProfile         string       "help:\"write cpu profile to `file`\""
    95  	Complete           bool         "help:\"compiling complete package (no C or assembly)\""
    96  	ClobberDead        bool         "help:\"clobber dead stack slots (for debugging)\""
    97  	ClobberDeadReg     bool         "help:\"clobber dead registers (for debugging)\""
    98  	Dwarf              bool         "help:\"generate DWARF symbols\""
    99  	DwarfBASEntries    *bool        "help:\"use base address selection entries in DWARF\""                        // &Ctxt.UseBASEntries, set below
   100  	DwarfLocationLists *bool        "help:\"add location lists to DWARF in optimized mode\""                      // &Ctxt.Flag_locationlists, set below
   101  	Dynlink            *bool        "help:\"support references to Go symbols defined in other shared libraries\"" // &Ctxt.Flag_dynlink, set below
   102  	EmbedCfg           func(string) "help:\"read go:embed configuration from `file`\""
   103  	Env                func(string) "help:\"add `definition` of the form key=value to environment\""
   104  	ExportFD           int          "help:\"write a byte to file descriptor `fd` once the export data has been written\""
   105  	GenDwarfInl        int          "help:\"generate DWARF inline info records\"" // 0=disabled, 1=funcs, 2=funcs+formals/locals
   106  	GoVersion          string       "help:\"required version of the runtime\""
   107  	ImportCfg          func(string) "help:\"read import configuration from `file`\""
   108  	InstallSuffix      string       "help:\"set pkg directory `suffix`\""
   109  	JSON               string       "help:\"version,file for JSON compiler/optimizer detail output\""
   110  	Lang               string       "help:\"Go language version source code expects\""
   111  	LinkObj            string       "help:\"write linker-specific object to `file`\""
   112  	LinkShared         *bool        "help:\"generate code that will be linked against Go shared libraries\"" // &Ctxt.Flag_linkshared, set below
   113  	Live               CountFlag    "help:\"debug liveness analysis\""
   114  	MSan               bool         "help:\"build code compatible with C/C++ memory sanitizer\""
   115  	MemProfile         string       "help:\"write memory profile to `file`\""
   116  	MemProfileRate     int          "help:\"set runtime.MemProfileRate to `rate`\""
   117  	MutexProfile       string       "help:\"write mutex profile to `file`\""
   118  	NoLocalImports     bool         "help:\"reject local (relative) imports\""
   119  	CoverageCfg        func(string) "help:\"read coverage configuration from `file`\""
   120  	Pack               bool         "help:\"write to file.a instead of file.o\""
   121  	Race               bool         "help:\"enable race detector\""
   122  	Shared             *bool        "help:\"generate code that can be linked into a shared library\"" // &Ctxt.Flag_shared, set below
   123  	SmallFrames        bool         "help:\"reduce the size limit for stack allocated objects\""      // small stacks, to diagnose GC latency; see golang.org/issue/27732
   124  	Spectre            string       "help:\"enable spectre mitigations in `list` (all, index, ret)\""
   125  	Stats              bool         "help:\"print compiler statistics\""
   126  	Std                bool         "help:\"compiling standard library\""
   127  	SymABIs            string       "help:\"read symbol ABIs from `file`\""
   128  	TraceProfile       string       "help:\"write an execution trace to `file`\""
   129  	TrimPath           string       "help:\"remove `prefix` from recorded source file paths\""
   130  	WB                 bool         "help:\"enable write barrier\"" // TODO: remove
   131  	PgoProfile         string       "help:\"read profile or pre-process profile from `file`\""
   132  	ErrorURL           bool         "help:\"print explanatory URL with error message if applicable\""
   133  
   134  	// Configuration derived from flags; not a flag itself.
   135  	Cfg struct {
   136  		Embed struct { // set by -embedcfg
   137  			Patterns map[string][]string
   138  			Files    map[string]string
   139  		}
   140  		ImportDirs   []string                 // appended to by -I
   141  		ImportMap    map[string]string        // set by -importcfg
   142  		PackageFile  map[string]string        // set by -importcfg; nil means not in use
   143  		CoverageInfo *covcmd.CoverFixupConfig // set by -coveragecfg
   144  		SpectreIndex bool                     // set by -spectre=index or -spectre=all
   145  		// Whether we are adding any sort of code instrumentation, such as
   146  		// when the race detector is enabled.
   147  		Instrumenting bool
   148  	}
   149  }
   150  
   151  func addEnv(s string) {
   152  	i := strings.Index(s, "=")
   153  	if i < 0 {
   154  		log.Fatal("-env argument must be of the form key=value")
   155  	}
   156  	os.Setenv(s[:i], s[i+1:])
   157  }
   158  
   159  // ParseFlags parses the command-line flags into Flag.
   160  func ParseFlags() {
   161  	Flag.I = addImportDir
   162  
   163  	Flag.LowerC = runtime.GOMAXPROCS(0)
   164  	Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA)
   165  	Flag.LowerP = &Ctxt.Pkgpath
   166  	Flag.LowerV = &Ctxt.Debugvlog
   167  
   168  	Flag.Dwarf = buildcfg.GOARCH != "wasm"
   169  	Flag.DwarfBASEntries = &Ctxt.UseBASEntries
   170  	Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
   171  	*Flag.DwarfLocationLists = true
   172  	Flag.Dynlink = &Ctxt.Flag_dynlink
   173  	Flag.EmbedCfg = readEmbedCfg
   174  	Flag.Env = addEnv
   175  	Flag.GenDwarfInl = 2
   176  	Flag.ImportCfg = readImportCfg
   177  	Flag.CoverageCfg = readCoverageCfg
   178  	Flag.LinkShared = &Ctxt.Flag_linkshared
   179  	Flag.Shared = &Ctxt.Flag_shared
   180  	Flag.WB = true
   181  
   182  	Debug.ConcurrentOk = true
   183  	Debug.CompressInstructions = 1
   184  	Debug.MaxShapeLen = 500
   185  	Debug.AlignHot = 1
   186  	Debug.InlFuncsWithClosures = 1
   187  	Debug.InlStaticInit = 1
   188  	Debug.FreeAppend = 1
   189  	Debug.PGOInline = 1
   190  	Debug.PGODevirtualize = 2
   191  	Debug.SyncFrames = -1            // disable sync markers by default
   192  	Debug.VariableMakeThreshold = 32 // 32 byte default for stack allocated make results
   193  	Debug.ZeroCopy = 1
   194  	Debug.RangeFuncCheck = 1
   195  	Debug.MergeLocals = 1
   196  	Debug.RewriteResults = 1
   197  
   198  	Debug.Checkptr = -1 // so we can tell whether it is set explicitly
   199  
   200  	Flag.Cfg.ImportMap = make(map[string]string)
   201  
   202  	objabi.AddVersionFlag() // -V
   203  	registerFlags()
   204  	objabi.Flagparse(usage)
   205  	counter.CountFlags("compile/flag:", *flag.CommandLine)
   206  
   207  	if gcd := os.Getenv("GOCOMPILEDEBUG"); gcd != "" {
   208  		// This will only override the flags set in gcd;
   209  		// any others set on the command line remain set.
   210  		Flag.LowerD.Set(gcd)
   211  	}
   212  
   213  	if Debug.Gossahash != "" {
   214  		hashDebug = NewHashDebug("gossahash", Debug.Gossahash, nil)
   215  	}
   216  	obj.SetFIPSDebugHash(Debug.FIPSHash)
   217  
   218  	// Compute whether we're compiling the runtime from the package path. Test
   219  	// code can also use the flag to set this explicitly.
   220  	if Flag.Std && objabi.LookupPkgSpecial(Ctxt.Pkgpath).Runtime {
   221  		Flag.CompilingRuntime = true
   222  	}
   223  
   224  	Ctxt.Std = Flag.Std
   225  
   226  	// Three inputs govern loop iteration variable rewriting, hash, experiment, flag.
   227  	// The loop variable rewriting is:
   228  	// IF non-empty hash, then hash determines behavior (function+line match) (*)
   229  	// ELSE IF experiment and flag==0, then experiment (set flag=1)
   230  	// ELSE flag (note that build sets flag per-package), with behaviors:
   231  	//  -1 => no change to behavior.
   232  	//   0 => no change to behavior (unless non-empty hash, see above)
   233  	//   1 => apply change to likely-iteration-variable-escaping loops
   234  	//   2 => apply change, log results
   235  	//   11 => apply change EVERYWHERE, do not log results (for debugging/benchmarking)
   236  	//   12 => apply change EVERYWHERE, log results (for debugging/benchmarking)
   237  	//
   238  	// The expected uses of the these inputs are, in believed most-likely to least likely:
   239  	//  GOEXPERIMENT=loopvar -- apply change to entire application
   240  	//  -gcflags=some_package=-d=loopvar=1 -- apply change to some_package (**)
   241  	//  -gcflags=some_package=-d=loopvar=2 -- apply change to some_package, log it
   242  	//  GOEXPERIMENT=loopvar -gcflags=some_package=-d=loopvar=-1 -- apply change to all but one package
   243  	//  GOCOMPILEDEBUG=loopvarhash=... -- search for failure cause
   244  	//
   245  	//  (*) For debugging purposes, providing loopvar flag >= 11 will expand the hash-eligible set of loops to all.
   246  	// (**) Loop semantics, changed or not, follow code from a package when it is inlined; that is, the behavior
   247  	//      of an application compiled with partially modified loop semantics does not depend on inlining.
   248  
   249  	if Debug.LoopVarHash != "" {
   250  		// This first little bit controls the inputs for debug-hash-matching.
   251  		mostInlineOnly := true
   252  		if strings.HasPrefix(Debug.LoopVarHash, "IL") {
   253  			// When hash-searching on a position that is an inline site, default is to use the
   254  			// most-inlined position only.  This makes the hash faster, plus there's no point
   255  			// reporting a problem with all the inlining; there's only one copy of the source.
   256  			// However, if for some reason you wanted it per-site, you can get this.  (The default
   257  			// hash-search behavior for compiler debugging is at an inline site.)
   258  			Debug.LoopVarHash = Debug.LoopVarHash[2:]
   259  			mostInlineOnly = false
   260  		}
   261  		// end of testing trickiness
   262  		LoopVarHash = NewHashDebug("loopvarhash", Debug.LoopVarHash, nil)
   263  		if Debug.LoopVar < 11 { // >= 11 means all loops are rewrite-eligible
   264  			Debug.LoopVar = 1 // 1 means those loops that syntactically escape their dcl vars are eligible.
   265  		}
   266  		LoopVarHash.SetInlineSuffixOnly(mostInlineOnly)
   267  	} else if buildcfg.Experiment.LoopVar && Debug.LoopVar == 0 {
   268  		Debug.LoopVar = 1
   269  	}
   270  
   271  	if Debug.Converthash != "" {
   272  		ConvertHash = NewHashDebug("converthash", Debug.Converthash, nil)
   273  	} else {
   274  		// quietly disable the convert hash changes
   275  		ConvertHash = NewHashDebug("converthash", "qn", nil)
   276  	}
   277  	if Debug.Fmahash != "" {
   278  		FmaHash = NewHashDebug("fmahash", Debug.Fmahash, nil)
   279  	}
   280  	if Debug.PGOHash != "" {
   281  		PGOHash = NewHashDebug("pgohash", Debug.PGOHash, nil)
   282  	}
   283  	if Debug.LiteralAllocHash != "" {
   284  		LiteralAllocHash = NewHashDebug("literalalloc", Debug.LiteralAllocHash, nil)
   285  	}
   286  
   287  	if Debug.MergeLocalsHash != "" {
   288  		MergeLocalsHash = NewHashDebug("mergelocals", Debug.MergeLocalsHash, nil)
   289  	}
   290  	if Debug.VariableMakeHash != "" {
   291  		VariableMakeHash = NewHashDebug("variablemake", Debug.VariableMakeHash, nil)
   292  	}
   293  
   294  	if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   295  		log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
   296  	}
   297  	if Flag.ASan && !platform.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   298  		log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
   299  	}
   300  	if Flag.Race && !platform.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   301  		log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
   302  	}
   303  	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) {
   304  		log.Fatalf("%s/%s does not support -shared", buildcfg.GOOS, buildcfg.GOARCH)
   305  	}
   306  	parseSpectre(Flag.Spectre) // left as string for RecordFlags
   307  
   308  	Ctxt.CompressInstructions = Debug.CompressInstructions != 0
   309  	Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
   310  	Ctxt.Flag_optimize = Flag.N == 0
   311  	Ctxt.Debugasm = int(Flag.S)
   312  	Ctxt.Flag_maymorestack = Debug.MayMoreStack
   313  	Ctxt.Flag_noRefName = Debug.NoRefName != 0
   314  
   315  	if flag.NArg() < 1 {
   316  		usage()
   317  	}
   318  
   319  	if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
   320  		fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
   321  		Exit(2)
   322  	}
   323  
   324  	if *Flag.LowerP == "" {
   325  		*Flag.LowerP = obj.UnlinkablePkg
   326  	}
   327  
   328  	if Flag.LowerO == "" {
   329  		p := flag.Arg(0)
   330  		if i := strings.LastIndex(p, "/"); i >= 0 {
   331  			p = p[i+1:]
   332  		}
   333  		if runtime.GOOS == "windows" {
   334  			if i := strings.LastIndex(p, `\`); i >= 0 {
   335  				p = p[i+1:]
   336  			}
   337  		}
   338  		if i := strings.LastIndex(p, "."); i >= 0 {
   339  			p = p[:i]
   340  		}
   341  		suffix := ".o"
   342  		if Flag.Pack {
   343  			suffix = ".a"
   344  		}
   345  		Flag.LowerO = p + suffix
   346  	}
   347  	switch {
   348  	case Flag.Race && Flag.MSan:
   349  		log.Fatal("cannot use both -race and -msan")
   350  	case Flag.Race && Flag.ASan:
   351  		log.Fatal("cannot use both -race and -asan")
   352  	case Flag.MSan && Flag.ASan:
   353  		log.Fatal("cannot use both -msan and -asan")
   354  	}
   355  	if Flag.Race || Flag.MSan || Flag.ASan {
   356  		// -race, -msan and -asan imply -d=checkptr for now.
   357  		if Debug.Checkptr == -1 { // if not set explicitly
   358  			Debug.Checkptr = 1
   359  		}
   360  	}
   361  
   362  	if Flag.LowerC < 1 {
   363  		log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
   364  	}
   365  	if Flag.ExportFD > 0 && Flag.LinkObj == "" {
   366  		log.Fatalf("-exportfd requires -linkobj")
   367  	}
   368  	if !concurrentBackendAllowed() {
   369  		Flag.LowerC = 1
   370  	}
   371  
   372  	if Flag.CompilingRuntime {
   373  		// It is not possible to build the runtime with no optimizations,
   374  		// because the compiler cannot eliminate enough write barriers.
   375  		Flag.N = 0
   376  		Ctxt.Flag_optimize = true
   377  
   378  		// Runtime can't use -d=checkptr, at least not yet.
   379  		Debug.Checkptr = 0
   380  
   381  		// Fuzzing the runtime isn't interesting either.
   382  		Debug.Libfuzzer = 0
   383  	}
   384  
   385  	if len(Flag.Cfg.ImportDirs) > 0 && Flag.Cfg.PackageFile != nil {
   386  		log.Fatalf("cannot use both -I and -importcfg")
   387  	}
   388  
   389  	if Debug.Checkptr == -1 { // if not set explicitly
   390  		Debug.Checkptr = 0
   391  	}
   392  
   393  	// set via a -d flag
   394  	Ctxt.Debugpcln = Debug.PCTab
   395  
   396  	// https://golang.org/issue/67502
   397  	if buildcfg.GOOS == "plan9" && buildcfg.GOARCH == "386" {
   398  		Debug.AlignHot = 0
   399  	}
   400  }
   401  
   402  // registerFlags adds flag registrations for all the fields in Flag.
   403  // See the comment on type CmdFlags for the rules.
   404  func registerFlags() {
   405  	var (
   406  		boolType      = reflect.TypeFor[bool]()
   407  		intType       = reflect.TypeFor[int]()
   408  		stringType    = reflect.TypeFor[string]()
   409  		ptrBoolType   = reflect.TypeFor[*bool]()
   410  		ptrIntType    = reflect.TypeFor[*int]()
   411  		ptrStringType = reflect.TypeFor[*string]()
   412  		countType     = reflect.TypeFor[CountFlag]()
   413  		funcType      = reflect.TypeFor[func(string)]()
   414  	)
   415  
   416  	v := reflect.ValueOf(&Flag).Elem()
   417  	t := v.Type()
   418  	for i := 0; i < t.NumField(); i++ {
   419  		f := t.Field(i)
   420  		if f.Name == "Cfg" {
   421  			continue
   422  		}
   423  
   424  		var name string
   425  		if len(f.Name) == 1 {
   426  			name = f.Name
   427  		} else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
   428  			name = string(rune(f.Name[5] + 'a' - 'A'))
   429  		} else {
   430  			name = strings.ToLower(f.Name)
   431  		}
   432  		if tag := f.Tag.Get("flag"); tag != "" {
   433  			name = tag
   434  		}
   435  
   436  		help := f.Tag.Get("help")
   437  		if help == "" {
   438  			panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
   439  		}
   440  
   441  		if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
   442  			panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
   443  		}
   444  
   445  		switch f.Type {
   446  		case boolType:
   447  			p := v.Field(i).Addr().Interface().(*bool)
   448  			flag.BoolVar(p, name, *p, help)
   449  		case intType:
   450  			p := v.Field(i).Addr().Interface().(*int)
   451  			flag.IntVar(p, name, *p, help)
   452  		case stringType:
   453  			p := v.Field(i).Addr().Interface().(*string)
   454  			flag.StringVar(p, name, *p, help)
   455  		case ptrBoolType:
   456  			p := v.Field(i).Interface().(*bool)
   457  			flag.BoolVar(p, name, *p, help)
   458  		case ptrIntType:
   459  			p := v.Field(i).Interface().(*int)
   460  			flag.IntVar(p, name, *p, help)
   461  		case ptrStringType:
   462  			p := v.Field(i).Interface().(*string)
   463  			flag.StringVar(p, name, *p, help)
   464  		case countType:
   465  			p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
   466  			objabi.Flagcount(name, help, p)
   467  		case funcType:
   468  			f := v.Field(i).Interface().(func(string))
   469  			objabi.Flagfn1(name, help, f)
   470  		default:
   471  			if val, ok := v.Field(i).Interface().(flag.Value); ok {
   472  				flag.Var(val, name, help)
   473  			} else {
   474  				panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
   475  			}
   476  		}
   477  	}
   478  }
   479  
   480  // concurrentFlagOk reports whether the current compiler flags
   481  // are compatible with concurrent compilation.
   482  func concurrentFlagOk() bool {
   483  	// TODO(rsc): Many of these are fine. Remove them.
   484  	return Flag.Percent == 0 &&
   485  		Flag.E == 0 &&
   486  		Flag.K == 0 &&
   487  		Flag.L == 0 &&
   488  		Flag.LowerJ == 0 &&
   489  		Flag.LowerM == 0 &&
   490  		Flag.LowerR == 0
   491  }
   492  
   493  func concurrentBackendAllowed() bool {
   494  	if !concurrentFlagOk() {
   495  		return false
   496  	}
   497  
   498  	// Debug.S by itself is ok, because all printing occurs
   499  	// while writing the object file, and that is non-concurrent.
   500  	// Adding Debug_vlog, however, causes Debug.S to also print
   501  	// while flushing the plist, which happens concurrently.
   502  	if Ctxt.Debugvlog || !Debug.ConcurrentOk || Flag.Live > 0 {
   503  		return false
   504  	}
   505  	// TODO: Test and delete this condition.
   506  	if buildcfg.Experiment.FieldTrack {
   507  		return false
   508  	}
   509  	// TODO: fix races and enable the following flags
   510  	if Ctxt.Flag_dynlink || Flag.Race {
   511  		return false
   512  	}
   513  	return true
   514  }
   515  
   516  func addImportDir(dir string) {
   517  	if dir != "" {
   518  		Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
   519  	}
   520  }
   521  
   522  func readImportCfg(file string) {
   523  	if Flag.Cfg.ImportMap == nil {
   524  		Flag.Cfg.ImportMap = make(map[string]string)
   525  	}
   526  	Flag.Cfg.PackageFile = map[string]string{}
   527  	data, err := os.ReadFile(file)
   528  	if err != nil {
   529  		log.Fatalf("-importcfg: %v", err)
   530  	}
   531  
   532  	for lineNum, line := range strings.Split(string(data), "\n") {
   533  		lineNum++ // 1-based
   534  		line = strings.TrimSpace(line)
   535  		if line == "" || strings.HasPrefix(line, "#") {
   536  			continue
   537  		}
   538  
   539  		verb, args, found := strings.Cut(line, " ")
   540  		if found {
   541  			args = strings.TrimSpace(args)
   542  		}
   543  		before, after, hasEq := strings.Cut(args, "=")
   544  
   545  		switch verb {
   546  		default:
   547  			log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
   548  		case "importmap":
   549  			if !hasEq || before == "" || after == "" {
   550  				log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
   551  			}
   552  			Flag.Cfg.ImportMap[before] = after
   553  		case "packagefile":
   554  			if !hasEq || before == "" || after == "" {
   555  				log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
   556  			}
   557  			Flag.Cfg.PackageFile[before] = after
   558  		}
   559  	}
   560  }
   561  
   562  func readCoverageCfg(file string) {
   563  	var cfg covcmd.CoverFixupConfig
   564  	data, err := os.ReadFile(file)
   565  	if err != nil {
   566  		log.Fatalf("-coveragecfg: %v", err)
   567  	}
   568  	if err := json.Unmarshal(data, &cfg); err != nil {
   569  		log.Fatalf("error reading -coveragecfg file %q: %v", file, err)
   570  	}
   571  	Flag.Cfg.CoverageInfo = &cfg
   572  }
   573  
   574  func readEmbedCfg(file string) {
   575  	data, err := os.ReadFile(file)
   576  	if err != nil {
   577  		log.Fatalf("-embedcfg: %v", err)
   578  	}
   579  	if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
   580  		log.Fatalf("%s: %v", file, err)
   581  	}
   582  	if Flag.Cfg.Embed.Patterns == nil {
   583  		log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
   584  	}
   585  	if Flag.Cfg.Embed.Files == nil {
   586  		log.Fatalf("%s: invalid embedcfg: missing Files", file)
   587  	}
   588  }
   589  
   590  // parseSpectre parses the spectre configuration from the string s.
   591  func parseSpectre(s string) {
   592  	for f := range strings.SplitSeq(s, ",") {
   593  		f = strings.TrimSpace(f)
   594  		switch f {
   595  		default:
   596  			log.Fatalf("unknown setting -spectre=%s", f)
   597  		case "":
   598  			// nothing
   599  		case "all":
   600  			Flag.Cfg.SpectreIndex = true
   601  			Ctxt.Retpoline = true
   602  		case "index":
   603  			Flag.Cfg.SpectreIndex = true
   604  		case "ret":
   605  			Ctxt.Retpoline = true
   606  		}
   607  	}
   608  
   609  	if Flag.Cfg.SpectreIndex {
   610  		switch buildcfg.GOARCH {
   611  		case "amd64":
   612  			// ok
   613  		default:
   614  			log.Fatalf("GOARCH=%s does not support -spectre=index", buildcfg.GOARCH)
   615  		}
   616  	}
   617  }
   618  

View as plain text