github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/base/flag.go (about)

     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  	"github.com/bir3/gocompiler/src/cmd/compile/flag"
    10  	"fmt"
    11  	"github.com/bir3/gocompiler/src/internal/buildcfg"
    12  	"github.com/bir3/gocompiler/src/internal/coverage"
    13  	"github.com/bir3/gocompiler/src/internal/platform"
    14  	"log"
    15  	"os"
    16  	"reflect"
    17  	"runtime"
    18  	"strings"
    19  
    20  	"github.com/bir3/gocompiler/src/cmd/internal/obj"
    21  
    22  	"github.com/bir3/gocompiler/src/cmd/compile/flag_objabi"
    23  	"github.com/bir3/gocompiler/src/cmd/internal/sys"
    24  )
    25  
    26  func usage() {
    27  	fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
    28  	flag_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 flag_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  	LowerV *bool      "help:\"increase debug verbosity\""
    82  
    83  	// Special characters
    84  	Percent          CountFlag "flag:\"%\" help:\"debug non-static initializers\""
    85  	CompilingRuntime bool      "flag:\"+\" help:\"compiling runtime\""
    86  
    87  	// Longer names
    88  	AsmHdr             string       "help:\"write assembly header to `file`\""
    89  	ASan               bool         "help:\"build code compatible with C/C++ address sanitizer\""
    90  	Bench              string       "help:\"append benchmark times to `file`\""
    91  	BlockProfile       string       "help:\"write block profile to `file`\""
    92  	BuildID            string       "help:\"record `id` as the build id in the export metadata\""
    93  	CPUProfile         string       "help:\"write cpu profile to `file`\""
    94  	Complete           bool         "help:\"compiling complete package (no C or assembly)\""
    95  	ClobberDead        bool         "help:\"clobber dead stack slots (for debugging)\""
    96  	ClobberDeadReg     bool         "help:\"clobber dead registers (for debugging)\""
    97  	Dwarf              bool         "help:\"generate DWARF symbols\""
    98  	DwarfBASEntries    *bool        "help:\"use base address selection entries in DWARF\""                        // &Ctxt.UseBASEntries, set below
    99  	DwarfLocationLists *bool        "help:\"add location lists to DWARF in optimized mode\""                      // &Ctxt.Flag_locationlists, set below
   100  	Dynlink            *bool        "help:\"support references to Go symbols defined in other shared libraries\"" // &Ctxt.Flag_dynlink, set below
   101  	EmbedCfg           func(string) "help:\"read go:embed configuration from `file`\""
   102  	GenDwarfInl        int          "help:\"generate DWARF inline info records\"" // 0=disabled, 1=funcs, 2=funcs+formals/locals
   103  	GoVersion          string       "help:\"required version of the runtime\""
   104  	ImportCfg          func(string) "help:\"read import configuration from `file`\""
   105  	InstallSuffix      string       "help:\"set pkg directory `suffix`\""
   106  	JSON               string       "help:\"version,file for JSON compiler/optimizer detail output\""
   107  	Lang               string       "help:\"Go language version source code expects\""
   108  	LinkObj            string       "help:\"write linker-specific object to `file`\""
   109  	LinkShared         *bool        "help:\"generate code that will be linked against Go shared libraries\"" // &Ctxt.Flag_linkshared, set below
   110  	Live               CountFlag    "help:\"debug liveness analysis\""
   111  	MSan               bool         "help:\"build code compatible with C/C++ memory sanitizer\""
   112  	MemProfile         string       "help:\"write memory profile to `file`\""
   113  	MemProfileRate     int          "help:\"set runtime.MemProfileRate to `rate`\""
   114  	MutexProfile       string       "help:\"write mutex profile to `file`\""
   115  	NoLocalImports     bool         "help:\"reject local (relative) imports\""
   116  	CoverageCfg        func(string) "help:\"read coverage configuration from `file`\""
   117  	Pack               bool         "help:\"write to file.a instead of file.o\""
   118  	Race               bool         "help:\"enable race detector\""
   119  	Shared             *bool        "help:\"generate code that can be linked into a shared library\"" // &Ctxt.Flag_shared, set below
   120  	SmallFrames        bool         "help:\"reduce the size limit for stack allocated objects\""      // small stacks, to diagnose GC latency; see golang.org/issue/27732
   121  	Spectre            string       "help:\"enable spectre mitigations in `list` (all, index, ret)\""
   122  	Std                bool         "help:\"compiling standard library\""
   123  	SymABIs            string       "help:\"read symbol ABIs from `file`\""
   124  	TraceProfile       string       "help:\"write an execution trace to `file`\""
   125  	TrimPath           string       "help:\"remove `prefix` from recorded source file paths\""
   126  	WB                 bool         "help:\"enable write barrier\""            // TODO: remove
   127  	OldComparable      bool         "help:\"enable old comparable semantics\"" // TODO: remove for Go 1.21
   128  	PgoProfile         string       "help:\"read profile from `file`\""
   129  
   130  	// Configuration derived from flags; not a flag itself.
   131  	Cfg struct {
   132  		Embed struct { // set by -embedcfg
   133  			Patterns map[string][]string
   134  			Files    map[string]string
   135  		}
   136  		ImportDirs   []string                   // appended to by -I
   137  		ImportMap    map[string]string          // set by -importcfg
   138  		PackageFile  map[string]string          // set by -importcfg; nil means not in use
   139  		CoverageInfo *coverage.CoverFixupConfig // set by -coveragecfg
   140  		SpectreIndex bool                       // set by -spectre=index or -spectre=all
   141  		// Whether we are adding any sort of code instrumentation, such as
   142  		// when the race detector is enabled.
   143  		Instrumenting bool
   144  	}
   145  }
   146  
   147  // ParseFlags parses the command-line flags into Flag.
   148  func ParseFlags() {
   149  	Flag.I = addImportDir
   150  
   151  	Flag.LowerC = runtime.GOMAXPROCS(0)
   152  	Flag.LowerD = flag_objabi.NewDebugFlag(&Debug, DebugSSA)
   153  	Flag.LowerP = &Ctxt.Pkgpath
   154  	Flag.LowerV = &Ctxt.Debugvlog
   155  
   156  	Flag.Dwarf = buildcfg.GOARCH != "wasm"
   157  	Flag.DwarfBASEntries = &Ctxt.UseBASEntries
   158  	Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
   159  	*Flag.DwarfLocationLists = true
   160  	Flag.Dynlink = &Ctxt.Flag_dynlink
   161  	Flag.EmbedCfg = readEmbedCfg
   162  	Flag.GenDwarfInl = 2
   163  	Flag.ImportCfg = readImportCfg
   164  	Flag.CoverageCfg = readCoverageCfg
   165  	Flag.LinkShared = &Ctxt.Flag_linkshared
   166  	Flag.Shared = &Ctxt.Flag_shared
   167  	Flag.WB = true
   168  
   169  	Debug.ConcurrentOk = true
   170  	Debug.InlFuncsWithClosures = 1
   171  	Debug.InlStaticInit = 0
   172  	if buildcfg.Experiment.Unified {
   173  		Debug.Unified = 1
   174  	}
   175  	Debug.SyncFrames = -1 // disable sync markers by default
   176  
   177  	Debug.Checkptr = -1 // so we can tell whether it is set explicitly
   178  
   179  	Flag.Cfg.ImportMap = make(map[string]string)
   180  
   181  	flag_objabi.AddVersionFlag() // -V
   182  	registerFlags()
   183  	flag_objabi.Flagparse(usage)
   184  
   185  	if gcd := os.Getenv("GOCOMPILEDEBUG"); gcd != "" {
   186  		// This will only override the flags set in gcd;
   187  		// any others set on the command line remain set.
   188  		Flag.LowerD.Set(gcd)
   189  	}
   190  
   191  	if Debug.Gossahash != "" {
   192  		hashDebug = NewHashDebug("gosshash", Debug.Gossahash, nil)
   193  	}
   194  
   195  	if Debug.Fmahash != "" {
   196  		FmaHash = NewHashDebug("fmahash", Debug.Fmahash, nil)
   197  	}
   198  
   199  	if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   200  		log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
   201  	}
   202  	if Flag.ASan && !platform.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   203  		log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
   204  	}
   205  	if Flag.Race && !platform.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
   206  		log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
   207  	}
   208  	if (*Flag.Shared || *Flag.Dynlink || *Flag.LinkShared) && !Ctxt.Arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) {
   209  		log.Fatalf("%s/%s does not support -shared", buildcfg.GOOS, buildcfg.GOARCH)
   210  	}
   211  	parseSpectre(Flag.Spectre) // left as string for RecordFlags
   212  
   213  	Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
   214  	Ctxt.Flag_optimize = Flag.N == 0
   215  	Ctxt.Debugasm = int(Flag.S)
   216  	Ctxt.Flag_maymorestack = Debug.MayMoreStack
   217  	Ctxt.Flag_noRefName = Debug.NoRefName != 0
   218  
   219  	if flag.NArg() < 1 {
   220  		usage()
   221  	}
   222  
   223  	if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
   224  		fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
   225  		Exit(2)
   226  	}
   227  
   228  	if *Flag.LowerP == "" {
   229  		*Flag.LowerP = obj.UnlinkablePkg
   230  	}
   231  
   232  	if Flag.LowerO == "" {
   233  		p := flag.Arg(0)
   234  		if i := strings.LastIndex(p, "/"); i >= 0 {
   235  			p = p[i+1:]
   236  		}
   237  		if runtime.GOOS == "windows" {
   238  			if i := strings.LastIndex(p, `\`); i >= 0 {
   239  				p = p[i+1:]
   240  			}
   241  		}
   242  		if i := strings.LastIndex(p, "."); i >= 0 {
   243  			p = p[:i]
   244  		}
   245  		suffix := ".o"
   246  		if Flag.Pack {
   247  			suffix = ".a"
   248  		}
   249  		Flag.LowerO = p + suffix
   250  	}
   251  	switch {
   252  	case Flag.Race && Flag.MSan:
   253  		log.Fatal("cannot use both -race and -msan")
   254  	case Flag.Race && Flag.ASan:
   255  		log.Fatal("cannot use both -race and -asan")
   256  	case Flag.MSan && Flag.ASan:
   257  		log.Fatal("cannot use both -msan and -asan")
   258  	}
   259  	if Flag.Race || Flag.MSan || Flag.ASan {
   260  		// -race, -msan and -asan imply -d=checkptr for now.
   261  		if Debug.Checkptr == -1 { // if not set explicitly
   262  			Debug.Checkptr = 1
   263  		}
   264  	}
   265  
   266  	if Flag.CompilingRuntime && Flag.N != 0 {
   267  		log.Fatal("cannot disable optimizations while compiling runtime")
   268  	}
   269  	if Flag.LowerC < 1 {
   270  		log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
   271  	}
   272  	if !concurrentBackendAllowed() {
   273  		Flag.LowerC = 1
   274  	}
   275  
   276  	if Flag.CompilingRuntime {
   277  		// Runtime can't use -d=checkptr, at least not yet.
   278  		Debug.Checkptr = 0
   279  
   280  		// Fuzzing the runtime isn't interesting either.
   281  		Debug.Libfuzzer = 0
   282  	}
   283  
   284  	if Debug.Checkptr == -1 { // if not set explicitly
   285  		Debug.Checkptr = 0
   286  	}
   287  
   288  	// set via a -d flag
   289  	Ctxt.Debugpcln = Debug.PCTab
   290  }
   291  
   292  // registerFlags adds flag registrations for all the fields in Flag.
   293  // See the comment on type CmdFlags for the rules.
   294  func registerFlags() {
   295  	var (
   296  		boolType      = reflect.TypeOf(bool(false))
   297  		intType       = reflect.TypeOf(int(0))
   298  		stringType    = reflect.TypeOf(string(""))
   299  		ptrBoolType   = reflect.TypeOf(new(bool))
   300  		ptrIntType    = reflect.TypeOf(new(int))
   301  		ptrStringType = reflect.TypeOf(new(string))
   302  		countType     = reflect.TypeOf(CountFlag(0))
   303  		funcType      = reflect.TypeOf((func(string))(nil))
   304  	)
   305  
   306  	v := reflect.ValueOf(&Flag).Elem()
   307  	t := v.Type()
   308  	for i := 0; i < t.NumField(); i++ {
   309  		f := t.Field(i)
   310  		if f.Name == "Cfg" {
   311  			continue
   312  		}
   313  
   314  		var name string
   315  		if len(f.Name) == 1 {
   316  			name = f.Name
   317  		} else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
   318  			name = string(rune(f.Name[5] + 'a' - 'A'))
   319  		} else {
   320  			name = strings.ToLower(f.Name)
   321  		}
   322  		if tag := f.Tag.Get("flag"); tag != "" {
   323  			name = tag
   324  		}
   325  
   326  		help := f.Tag.Get("help")
   327  		if help == "" {
   328  			panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
   329  		}
   330  
   331  		if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
   332  			panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
   333  		}
   334  
   335  		switch f.Type {
   336  		case boolType:
   337  			p := v.Field(i).Addr().Interface().(*bool)
   338  			flag.BoolVar(p, name, *p, help)
   339  		case intType:
   340  			p := v.Field(i).Addr().Interface().(*int)
   341  			flag.IntVar(p, name, *p, help)
   342  		case stringType:
   343  			p := v.Field(i).Addr().Interface().(*string)
   344  			flag.StringVar(p, name, *p, help)
   345  		case ptrBoolType:
   346  			p := v.Field(i).Interface().(*bool)
   347  			flag.BoolVar(p, name, *p, help)
   348  		case ptrIntType:
   349  			p := v.Field(i).Interface().(*int)
   350  			flag.IntVar(p, name, *p, help)
   351  		case ptrStringType:
   352  			p := v.Field(i).Interface().(*string)
   353  			flag.StringVar(p, name, *p, help)
   354  		case countType:
   355  			p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
   356  			flag_objabi.Flagcount(name, help, p)
   357  		case funcType:
   358  			f := v.Field(i).Interface().(func(string))
   359  			flag_objabi.Flagfn1(name, help, f)
   360  		default:
   361  			if val, ok := v.Field(i).Interface().(flag.Value); ok {
   362  				flag.Var(val, name, help)
   363  			} else {
   364  				panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
   365  			}
   366  		}
   367  	}
   368  }
   369  
   370  // concurrentFlagOk reports whether the current compiler flags
   371  // are compatible with concurrent compilation.
   372  func concurrentFlagOk() bool {
   373  	// TODO(rsc): Many of these are fine. Remove them.
   374  	return Flag.Percent == 0 &&
   375  		Flag.E == 0 &&
   376  		Flag.K == 0 &&
   377  		Flag.L == 0 &&
   378  		Flag.LowerH == 0 &&
   379  		Flag.LowerJ == 0 &&
   380  		Flag.LowerM == 0 &&
   381  		Flag.LowerR == 0
   382  }
   383  
   384  func concurrentBackendAllowed() bool {
   385  	if !concurrentFlagOk() {
   386  		return false
   387  	}
   388  
   389  	// Debug.S by itself is ok, because all printing occurs
   390  	// while writing the object file, and that is non-concurrent.
   391  	// Adding Debug_vlog, however, causes Debug.S to also print
   392  	// while flushing the plist, which happens concurrently.
   393  	if Ctxt.Debugvlog || !Debug.ConcurrentOk || Flag.Live > 0 {
   394  		return false
   395  	}
   396  	// TODO: Test and delete this condition.
   397  	if buildcfg.Experiment.FieldTrack {
   398  		return false
   399  	}
   400  	// TODO: fix races and enable the following flags
   401  	if Ctxt.Flag_dynlink || Flag.Race {
   402  		return false
   403  	}
   404  	return true
   405  }
   406  
   407  func addImportDir(dir string) {
   408  	if dir != "" {
   409  		Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
   410  	}
   411  }
   412  
   413  func readImportCfg(file string) {
   414  	if Flag.Cfg.ImportMap == nil {
   415  		Flag.Cfg.ImportMap = make(map[string]string)
   416  	}
   417  	Flag.Cfg.PackageFile = map[string]string{}
   418  	data, err := os.ReadFile(file)
   419  	if err != nil {
   420  		log.Fatalf("-importcfg: %v", err)
   421  	}
   422  
   423  	for lineNum, line := range strings.Split(string(data), "\n") {
   424  		lineNum++ // 1-based
   425  		line = strings.TrimSpace(line)
   426  		if line == "" || strings.HasPrefix(line, "#") {
   427  			continue
   428  		}
   429  
   430  		var verb, args string
   431  		if i := strings.Index(line, " "); i < 0 {
   432  			verb = line
   433  		} else {
   434  			verb, args = line[:i], strings.TrimSpace(line[i+1:])
   435  		}
   436  		var before, after string
   437  		if i := strings.Index(args, "="); i >= 0 {
   438  			before, after = args[:i], args[i+1:]
   439  		}
   440  		switch verb {
   441  		default:
   442  			log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
   443  		case "importmap":
   444  			if before == "" || after == "" {
   445  				log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
   446  			}
   447  			Flag.Cfg.ImportMap[before] = after
   448  		case "packagefile":
   449  			if before == "" || after == "" {
   450  				log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
   451  			}
   452  			Flag.Cfg.PackageFile[before] = after
   453  		}
   454  	}
   455  }
   456  
   457  func readCoverageCfg(file string) {
   458  	var cfg coverage.CoverFixupConfig
   459  	data, err := os.ReadFile(file)
   460  	if err != nil {
   461  		log.Fatalf("-coveragecfg: %v", err)
   462  	}
   463  	if err := json.Unmarshal(data, &cfg); err != nil {
   464  		log.Fatalf("error reading -coveragecfg file %q: %v", file, err)
   465  	}
   466  	Flag.Cfg.CoverageInfo = &cfg
   467  }
   468  
   469  func readEmbedCfg(file string) {
   470  	data, err := os.ReadFile(file)
   471  	if err != nil {
   472  		log.Fatalf("-embedcfg: %v", err)
   473  	}
   474  	if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
   475  		log.Fatalf("%s: %v", file, err)
   476  	}
   477  	if Flag.Cfg.Embed.Patterns == nil {
   478  		log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
   479  	}
   480  	if Flag.Cfg.Embed.Files == nil {
   481  		log.Fatalf("%s: invalid embedcfg: missing Files", file)
   482  	}
   483  }
   484  
   485  // parseSpectre parses the spectre configuration from the string s.
   486  func parseSpectre(s string) {
   487  	for _, f := range strings.Split(s, ",") {
   488  		f = strings.TrimSpace(f)
   489  		switch f {
   490  		default:
   491  			log.Fatalf("unknown setting -spectre=%s", f)
   492  		case "":
   493  			// nothing
   494  		case "all":
   495  			Flag.Cfg.SpectreIndex = true
   496  			Ctxt.Retpoline = true
   497  		case "index":
   498  			Flag.Cfg.SpectreIndex = true
   499  		case "ret":
   500  			Ctxt.Retpoline = true
   501  		}
   502  	}
   503  
   504  	if Flag.Cfg.SpectreIndex {
   505  		switch buildcfg.GOARCH {
   506  		case "amd64":
   507  			// ok
   508  		default:
   509  			log.Fatalf("GOARCH=%s does not support -spectre=index", buildcfg.GOARCH)
   510  		}
   511  	}
   512  }