diff options
author | Russ Cox <rsc@golang.org> | 2020-11-25 12:44:11 -0500 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2020-11-25 13:15:41 -0500 |
commit | 5c2e14872c70a8ad9fd27033a2451531c6c00c0e (patch) | |
tree | 394a86bb4ae567d3e1a19ba67d51049ad504bfa0 /src/cmd/compile/internal/base | |
parent | 2c25cd5ba7772a97ee63787e3986b6ec231e8c3d (diff) | |
parent | 41f3af9d04362a56c1af186af134c704a03fa97b (diff) | |
download | go-5c2e14872c70a8ad9fd27033a2451531c6c00c0e.tar.gz go-5c2e14872c70a8ad9fd27033a2451531c6c00c0e.zip |
[dev.typeparams] merge dev.regabi 41f3af9d04 into dev.typeparams
This brings in the new ir.Node interface, replacing *gc.Node.
Change-Id: I82c623655eee08d77d623babf22ec4d91f9aa3cd
Diffstat (limited to 'src/cmd/compile/internal/base')
-rw-r--r-- | src/cmd/compile/internal/base/base.go | 28 | ||||
-rw-r--r-- | src/cmd/compile/internal/base/debug.go | 194 | ||||
-rw-r--r-- | src/cmd/compile/internal/base/flag.go | 454 | ||||
-rw-r--r-- | src/cmd/compile/internal/base/print.go | 260 |
4 files changed, 936 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/base/base.go b/src/cmd/compile/internal/base/base.go new file mode 100644 index 0000000000..e26b378472 --- /dev/null +++ b/src/cmd/compile/internal/base/base.go @@ -0,0 +1,28 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package base + +import ( + "os" + + "cmd/internal/obj" +) + +var Ctxt *obj.Link + +var atExitFuncs []func() + +func AtExit(f func()) { + atExitFuncs = append(atExitFuncs, f) +} + +func Exit(code int) { + for i := len(atExitFuncs) - 1; i >= 0; i-- { + f := atExitFuncs[i] + atExitFuncs = atExitFuncs[:i] + f() + } + os.Exit(code) +} diff --git a/src/cmd/compile/internal/base/debug.go b/src/cmd/compile/internal/base/debug.go new file mode 100644 index 0000000000..45a552a4d9 --- /dev/null +++ b/src/cmd/compile/internal/base/debug.go @@ -0,0 +1,194 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Debug arguments, set by -d flag. + +package base + +import ( + "fmt" + "log" + "os" + "reflect" + "strconv" + "strings" + + "cmd/internal/objabi" +) + +// Debug holds the parsed debugging configuration values. +var Debug = DebugFlags{ + Fieldtrack: &objabi.Fieldtrack_enabled, +} + +// DebugFlags defines the debugging configuration values (see var Debug). +// Each struct field is a different value, named for the lower-case of the field name. +// Each field must be an int or string and must have a `help` struct tag. +// +// The -d option takes a comma-separated list of settings. +// Each setting is name=value; for ints, name is short for name=1. +type DebugFlags struct { + Append int `help:"print information about append compilation"` + Checkptr int `help:"instrument unsafe pointer conversions"` + Closure int `help:"print information about closure compilation"` + CompileLater int `help:"compile functions as late as possible"` + DclStack int `help:"run internal dclstack check"` + Defer int `help:"print information about defer compilation"` + DisableNil int `help:"disable nil checks"` + DumpPtrs int `help:"show Node pointers values in dump output"` + DwarfInl int `help:"print information about DWARF inlined function creation"` + Export int `help:"print export data"` + Fieldtrack *int `help:"enable field tracking"` + GCProg int `help:"print dump of GC programs"` + Libfuzzer int `help:"enable coverage instrumentation for libfuzzer"` + LocationLists int `help:"print information about DWARF location list creation"` + Nil int `help:"print information about nil checks"` + PCTab string `help:"print named pc-value table"` + Panic int `help:"show all compiler panics"` + Slice int `help:"print information about slice compilation"` + SoftFloat int `help:"force compiler to emit soft-float code"` + TypeAssert int `help:"print information about type assertion inlining"` + TypecheckInl int `help:"eager typechecking of inline function bodies"` + WB int `help:"print information about write barriers"` + + any bool // set when any of the values have been set +} + +// Any reports whether any of the debug flags have been set. +func (d *DebugFlags) Any() bool { return d.any } + +type debugField struct { + name string + help string + val interface{} // *int or *string +} + +var debugTab []debugField + +func init() { + v := reflect.ValueOf(&Debug).Elem() + t := v.Type() + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if f.Name == "any" { + continue + } + name := strings.ToLower(f.Name) + help := f.Tag.Get("help") + if help == "" { + panic(fmt.Sprintf("base.Debug.%s is missing help text", f.Name)) + } + ptr := v.Field(i).Addr().Interface() + switch ptr.(type) { + default: + panic(fmt.Sprintf("base.Debug.%s has invalid type %v (must be int or string)", f.Name, f.Type)) + case *int, *string: + // ok + case **int: + ptr = *ptr.(**int) // record the *int itself + } + debugTab = append(debugTab, debugField{name, help, ptr}) + } +} + +// DebugSSA is called to set a -d ssa/... option. +// If nil, those options are reported as invalid options. +// If DebugSSA returns a non-empty string, that text is reported as a compiler error. +var DebugSSA func(phase, flag string, val int, valString string) string + +// parseDebug parses the -d debug string argument. +func parseDebug(debugstr string) { + // parse -d argument + if debugstr == "" { + return + } + Debug.any = true +Split: + for _, name := range strings.Split(debugstr, ",") { + if name == "" { + continue + } + // display help about the -d option itself and quit + if name == "help" { + fmt.Print(debugHelpHeader) + maxLen := len("ssa/help") + for _, t := range debugTab { + if len(t.name) > maxLen { + maxLen = len(t.name) + } + } + for _, t := range debugTab { + fmt.Printf("\t%-*s\t%s\n", maxLen, t.name, t.help) + } + // ssa options have their own help + fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging") + fmt.Print(debugHelpFooter) + os.Exit(0) + } + val, valstring, haveInt := 1, "", true + if i := strings.IndexAny(name, "=:"); i >= 0 { + var err error + name, valstring = name[:i], name[i+1:] + val, err = strconv.Atoi(valstring) + if err != nil { + val, haveInt = 1, false + } + } + for _, t := range debugTab { + if t.name != name { + continue + } + switch vp := t.val.(type) { + case nil: + // Ignore + case *string: + *vp = valstring + case *int: + if !haveInt { + log.Fatalf("invalid debug value %v", name) + } + *vp = val + default: + panic("bad debugtab type") + } + continue Split + } + // special case for ssa for now + if DebugSSA != nil && strings.HasPrefix(name, "ssa/") { + // expect form ssa/phase/flag + // e.g. -d=ssa/generic_cse/time + // _ in phase name also matches space + phase := name[4:] + flag := "debug" // default flag is debug + if i := strings.Index(phase, "/"); i >= 0 { + flag = phase[i+1:] + phase = phase[:i] + } + err := DebugSSA(phase, flag, val, valstring) + if err != "" { + log.Fatalf(err) + } + continue Split + } + log.Fatalf("unknown debug key -d %s\n", name) + } +} + +const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>] + +<key> is one of: + +` + +const debugHelpFooter = ` +<value> is key-specific. + +Key "checkptr" supports values: + "0": instrumentation disabled + "1": conversions involving unsafe.Pointer are instrumented + "2": conversions to unsafe.Pointer force heap allocation + +Key "pctab" supports values: + "pctospadj", "pctofile", "pctoline", "pctoinline", "pctopcdata" +` diff --git a/src/cmd/compile/internal/base/flag.go b/src/cmd/compile/internal/base/flag.go new file mode 100644 index 0000000000..240258d6b8 --- /dev/null +++ b/src/cmd/compile/internal/base/flag.go @@ -0,0 +1,454 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package base + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "reflect" + "runtime" + "strings" + + "cmd/internal/objabi" + "cmd/internal/sys" +) + +func usage() { + fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n") + objabi.Flagprint(os.Stderr) + Exit(2) +} + +// Flag holds the parsed command-line flags. +// See ParseFlag for non-zero defaults. +var Flag CmdFlags + +// A CountFlag is a counting integer flag. +// It accepts -name=value to set the value directly, +// but it also accepts -name with no =value to increment the count. +type CountFlag int + +// CmdFlags defines the command-line flags (see var Flag). +// Each struct field is a different flag, by default named for the lower-case of the field name. +// If the flag name is a single letter, the default flag name is left upper-case. +// If the flag name is "Lower" followed by a single letter, the default flag name is the lower-case of the last letter. +// +// If this default flag name can't be made right, the `flag` struct tag can be used to replace it, +// but this should be done only in exceptional circumstances: it helps everyone if the flag name +// is obvious from the field name when the flag is used elsewhere in the compiler sources. +// The `flag:"-"` struct tag makes a field invisible to the flag logic and should also be used sparingly. +// +// Each field must have a `help` struct tag giving the flag help message. +// +// The allowed field types are bool, int, string, pointers to those (for values stored elsewhere), +// CountFlag (for a counting flag), and func(string) (for a flag that uses special code for parsing). +type CmdFlags struct { + // Single letters + B CountFlag "help:\"disable bounds checking\"" + C CountFlag "help:\"disable printing of columns in error messages\"" + D string "help:\"set relative `path` for local imports\"" + E CountFlag "help:\"debug symbol export\"" + G CountFlag "help:\"accept generic code\"" + I func(string) "help:\"add `directory` to import search path\"" + K CountFlag "help:\"debug missing line numbers\"" + L CountFlag "help:\"show full file names in error messages\"" + N CountFlag "help:\"disable optimizations\"" + S CountFlag "help:\"print assembly listing\"" + // V is added by objabi.AddVersionFlag + W CountFlag "help:\"debug parse tree after type checking\"" + + LowerC int "help:\"concurrency during compilation (1 means no concurrency)\"" + LowerD func(string) "help:\"enable debugging settings; try -d help\"" + LowerE CountFlag "help:\"no limit on number of errors reported\"" + LowerH CountFlag "help:\"halt on error\"" + LowerJ CountFlag "help:\"debug runtime-initialized variables\"" + LowerL CountFlag "help:\"disable inlining\"" + LowerM CountFlag "help:\"print optimization decisions\"" + LowerO string "help:\"write output to `file`\"" + LowerP *string "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below + LowerR CountFlag "help:\"debug generated wrappers\"" + LowerT bool "help:\"enable tracing for debugging the compiler\"" + LowerW CountFlag "help:\"debug type checking\"" + LowerV *bool "help:\"increase debug verbosity\"" + + // Special characters + Percent int "flag:\"%\" help:\"debug non-static initializers\"" + CompilingRuntime bool "flag:\"+\" help:\"compiling runtime\"" + + // Longer names + AsmHdr string "help:\"write assembly header to `file`\"" + Bench string "help:\"append benchmark times to `file`\"" + BlockProfile string "help:\"write block profile to `file`\"" + BuildID string "help:\"record `id` as the build id in the export metadata\"" + CPUProfile string "help:\"write cpu profile to `file`\"" + Complete bool "help:\"compiling complete package (no C or assembly)\"" + Dwarf bool "help:\"generate DWARF symbols\"" + DwarfBASEntries *bool "help:\"use base address selection entries in DWARF\"" // &Ctxt.UseBASEntries, set below + DwarfLocationLists *bool "help:\"add location lists to DWARF in optimized mode\"" // &Ctxt.Flag_locationlists, set below + Dynlink *bool "help:\"support references to Go symbols defined in other shared libraries\"" // &Ctxt.Flag_dynlink, set below + EmbedCfg func(string) "help:\"read go:embed configuration from `file`\"" + GenDwarfInl int "help:\"generate DWARF inline info records\"" // 0=disabled, 1=funcs, 2=funcs+formals/locals + GoVersion string "help:\"required version of the runtime\"" + ImportCfg func(string) "help:\"read import configuration from `file`\"" + ImportMap func(string) "help:\"add `definition` of the form source=actual to import map\"" + InstallSuffix string "help:\"set pkg directory `suffix`\"" + JSON string "help:\"version,file for JSON compiler/optimizer detail output\"" + Lang string "help:\"Go language version source code expects\"" + LinkObj string "help:\"write linker-specific object to `file`\"" + LinkShared *bool "help:\"generate code that will be linked against Go shared libraries\"" // &Ctxt.Flag_linkshared, set below + Live CountFlag "help:\"debug liveness analysis\"" + MSan bool "help:\"build code compatible with C/C++ memory sanitizer\"" + MemProfile string "help:\"write memory profile to `file`\"" + MemProfileRate int64 "help:\"set runtime.MemProfileRate to `rate`\"" + MutexProfile string "help:\"write mutex profile to `file`\"" + NoLocalImports bool "help:\"reject local (relative) imports\"" + Pack bool "help:\"write to file.a instead of file.o\"" + Race bool "help:\"enable race detector\"" + Shared *bool "help:\"generate code that can be linked into a shared library\"" // &Ctxt.Flag_shared, set below + SmallFrames bool "help:\"reduce the size limit for stack allocated objects\"" // small stacks, to diagnose GC latency; see golang.org/issue/27732 + Spectre string "help:\"enable spectre mitigations in `list` (all, index, ret)\"" + Std bool "help:\"compiling standard library\"" + SymABIs string "help:\"read symbol ABIs from `file`\"" + TraceProfile string "help:\"write an execution trace to `file`\"" + TrimPath string "help:\"remove `prefix` from recorded source file paths\"" + WB bool "help:\"enable write barrier\"" // TODO: remove + + // Configuration derived from flags; not a flag itself. + Cfg struct { + Embed struct { // set by -embedcfg + Patterns map[string][]string + Files map[string]string + } + ImportDirs []string // appended to by -I + ImportMap map[string]string // set by -importmap OR -importcfg + PackageFile map[string]string // set by -importcfg; nil means not in use + SpectreIndex bool // set by -spectre=index or -spectre=all + } +} + +// ParseFlags parses the command-line flags into Flag. +func ParseFlags() { + Flag.I = addImportDir + + Flag.LowerC = 1 + Flag.LowerD = parseDebug + Flag.LowerP = &Ctxt.Pkgpath + Flag.LowerV = &Ctxt.Debugvlog + + Flag.Dwarf = objabi.GOARCH != "wasm" + Flag.DwarfBASEntries = &Ctxt.UseBASEntries + Flag.DwarfLocationLists = &Ctxt.Flag_locationlists + *Flag.DwarfLocationLists = true + Flag.Dynlink = &Ctxt.Flag_dynlink + Flag.EmbedCfg = readEmbedCfg + Flag.GenDwarfInl = 2 + Flag.ImportCfg = readImportCfg + Flag.ImportMap = addImportMap + Flag.LinkShared = &Ctxt.Flag_linkshared + Flag.Shared = &Ctxt.Flag_shared + Flag.WB = true + + Flag.Cfg.ImportMap = make(map[string]string) + + objabi.AddVersionFlag() // -V + registerFlags() + objabi.Flagparse(usage) + + if Flag.MSan && !sys.MSanSupported(objabi.GOOS, objabi.GOARCH) { + log.Fatalf("%s/%s does not support -msan", objabi.GOOS, objabi.GOARCH) + } + if Flag.Race && !sys.RaceDetectorSupported(objabi.GOOS, objabi.GOARCH) { + log.Fatalf("%s/%s does not support -race", objabi.GOOS, objabi.GOARCH) + } + if (*Flag.Shared || *Flag.Dynlink || *Flag.LinkShared) && !Ctxt.Arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) { + log.Fatalf("%s/%s does not support -shared", objabi.GOOS, objabi.GOARCH) + } + parseSpectre(Flag.Spectre) // left as string for recordFlags + + Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared + Ctxt.Flag_optimize = Flag.N == 0 + Ctxt.Debugasm = int(Flag.S) + + if flag.NArg() < 1 { + usage() + } + + if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() { + fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion) + Exit(2) + } + + if Flag.LowerO == "" { + p := flag.Arg(0) + if i := strings.LastIndex(p, "/"); i >= 0 { + p = p[i+1:] + } + if runtime.GOOS == "windows" { + if i := strings.LastIndex(p, `\`); i >= 0 { + p = p[i+1:] + } + } + if i := strings.LastIndex(p, "."); i >= 0 { + p = p[:i] + } + suffix := ".o" + if Flag.Pack { + suffix = ".a" + } + Flag.LowerO = p + suffix + } + + if Flag.Race && Flag.MSan { + log.Fatal("cannot use both -race and -msan") + } + if Flag.Race || Flag.MSan { + // -race and -msan imply -d=checkptr for now. + Debug.Checkptr = 1 + } + + if Flag.CompilingRuntime && Flag.N != 0 { + log.Fatal("cannot disable optimizations while compiling runtime") + } + if Flag.LowerC < 1 { + log.Fatalf("-c must be at least 1, got %d", Flag.LowerC) + } + if Flag.LowerC > 1 && !concurrentBackendAllowed() { + log.Fatalf("cannot use concurrent backend compilation with provided flags; invoked as %v", os.Args) + } + + if Flag.CompilingRuntime { + // Runtime can't use -d=checkptr, at least not yet. + Debug.Checkptr = 0 + + // Fuzzing the runtime isn't interesting either. + Debug.Libfuzzer = 0 + } + + // set via a -d flag + Ctxt.Debugpcln = Debug.PCTab +} + +// registerFlags adds flag registrations for all the fields in Flag. +// See the comment on type CmdFlags for the rules. +func registerFlags() { + var ( + boolType = reflect.TypeOf(bool(false)) + intType = reflect.TypeOf(int(0)) + stringType = reflect.TypeOf(string("")) + ptrBoolType = reflect.TypeOf(new(bool)) + ptrIntType = reflect.TypeOf(new(int)) + ptrStringType = reflect.TypeOf(new(string)) + countType = reflect.TypeOf(CountFlag(0)) + funcType = reflect.TypeOf((func(string))(nil)) + ) + + v := reflect.ValueOf(&Flag).Elem() + t := v.Type() + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if f.Name == "Cfg" { + continue + } + + var name string + if len(f.Name) == 1 { + name = f.Name + } else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' { + name = string(rune(f.Name[5] + 'a' - 'A')) + } else { + name = strings.ToLower(f.Name) + } + if tag := f.Tag.Get("flag"); tag != "" { + name = tag + } + + help := f.Tag.Get("help") + if help == "" { + panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name)) + } + + if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() { + panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type)) + } + + switch f.Type { + case boolType: + p := v.Field(i).Addr().Interface().(*bool) + flag.BoolVar(p, name, *p, help) + case intType: + p := v.Field(i).Addr().Interface().(*int) + flag.IntVar(p, name, *p, help) + case stringType: + p := v.Field(i).Addr().Interface().(*string) + flag.StringVar(p, name, *p, help) + case ptrBoolType: + p := v.Field(i).Interface().(*bool) + flag.BoolVar(p, name, *p, help) + case ptrIntType: + p := v.Field(i).Interface().(*int) + flag.IntVar(p, name, *p, help) + case ptrStringType: + p := v.Field(i).Interface().(*string) + flag.StringVar(p, name, *p, help) + case countType: + p := (*int)(v.Field(i).Addr().Interface().(*CountFlag)) + objabi.Flagcount(name, help, p) + case funcType: + f := v.Field(i).Interface().(func(string)) + objabi.Flagfn1(name, help, f) + } + } +} + +// concurrentFlagOk reports whether the current compiler flags +// are compatible with concurrent compilation. +func concurrentFlagOk() bool { + // TODO(rsc): Many of these are fine. Remove them. + return Flag.Percent == 0 && + Flag.E == 0 && + Flag.K == 0 && + Flag.L == 0 && + Flag.LowerH == 0 && + Flag.LowerJ == 0 && + Flag.LowerM == 0 && + Flag.LowerR == 0 +} + +func concurrentBackendAllowed() bool { + if !concurrentFlagOk() { + return false + } + + // Debug.S by itself is ok, because all printing occurs + // while writing the object file, and that is non-concurrent. + // Adding Debug_vlog, however, causes Debug.S to also print + // while flushing the plist, which happens concurrently. + if Ctxt.Debugvlog || Debug.Any() || Flag.Live > 0 { + return false + } + // TODO: Test and delete this condition. + if objabi.Fieldtrack_enabled != 0 { + return false + } + // TODO: fix races and enable the following flags + if Ctxt.Flag_shared || Ctxt.Flag_dynlink || Flag.Race { + return false + } + return true +} + +func addImportDir(dir string) { + if dir != "" { + Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir) + } +} + +func addImportMap(s string) { + if Flag.Cfg.ImportMap == nil { + Flag.Cfg.ImportMap = make(map[string]string) + } + if strings.Count(s, "=") != 1 { + log.Fatal("-importmap argument must be of the form source=actual") + } + i := strings.Index(s, "=") + source, actual := s[:i], s[i+1:] + if source == "" || actual == "" { + log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty") + } + Flag.Cfg.ImportMap[source] = actual +} + +func readImportCfg(file string) { + if Flag.Cfg.ImportMap == nil { + Flag.Cfg.ImportMap = make(map[string]string) + } + Flag.Cfg.PackageFile = map[string]string{} + data, err := ioutil.ReadFile(file) + if err != nil { + log.Fatalf("-importcfg: %v", err) + } + + for lineNum, line := range strings.Split(string(data), "\n") { + lineNum++ // 1-based + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + + var verb, args string + if i := strings.Index(line, " "); i < 0 { + verb = line + } else { + verb, args = line[:i], strings.TrimSpace(line[i+1:]) + } + var before, after string + if i := strings.Index(args, "="); i >= 0 { + before, after = args[:i], args[i+1:] + } + switch verb { + default: + log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb) + case "importmap": + if before == "" || after == "" { + log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum) + } + Flag.Cfg.ImportMap[before] = after + case "packagefile": + if before == "" || after == "" { + log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum) + } + Flag.Cfg.PackageFile[before] = after + } + } +} + +func readEmbedCfg(file string) { + data, err := ioutil.ReadFile(file) + if err != nil { + log.Fatalf("-embedcfg: %v", err) + } + if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil { + log.Fatalf("%s: %v", file, err) + } + if Flag.Cfg.Embed.Patterns == nil { + log.Fatalf("%s: invalid embedcfg: missing Patterns", file) + } + if Flag.Cfg.Embed.Files == nil { + log.Fatalf("%s: invalid embedcfg: missing Files", file) + } +} + +// parseSpectre parses the spectre configuration from the string s. +func parseSpectre(s string) { + for _, f := range strings.Split(s, ",") { + f = strings.TrimSpace(f) + switch f { + default: + log.Fatalf("unknown setting -spectre=%s", f) + case "": + // nothing + case "all": + Flag.Cfg.SpectreIndex = true + Ctxt.Retpoline = true + case "index": + Flag.Cfg.SpectreIndex = true + case "ret": + Ctxt.Retpoline = true + } + } + + if Flag.Cfg.SpectreIndex { + switch objabi.GOARCH { + case "amd64": + // ok + default: + log.Fatalf("GOARCH=%s does not support -spectre=index", objabi.GOARCH) + } + } +} diff --git a/src/cmd/compile/internal/base/print.go b/src/cmd/compile/internal/base/print.go new file mode 100644 index 0000000000..6831b3ada3 --- /dev/null +++ b/src/cmd/compile/internal/base/print.go @@ -0,0 +1,260 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package base + +import ( + "fmt" + "os" + "runtime/debug" + "sort" + "strings" + + "cmd/internal/objabi" + "cmd/internal/src" +) + +// An errorMsg is a queued error message, waiting to be printed. +type errorMsg struct { + pos src.XPos + msg string +} + +// Pos is the current source position being processed, +// printed by Errorf, ErrorfLang, Fatalf, and Warnf. +var Pos src.XPos + +var ( + errorMsgs []errorMsg + numErrors int // number of entries in errorMsgs that are errors (as opposed to warnings) + numSyntaxErrors int +) + +// Errors returns the number of errors reported. +func Errors() int { + return numErrors +} + +// SyntaxErrors returns the number of syntax errors reported +func SyntaxErrors() int { + return numSyntaxErrors +} + +// addErrorMsg adds a new errorMsg (which may be a warning) to errorMsgs. +func addErrorMsg(pos src.XPos, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + // Only add the position if know the position. + // See issue golang.org/issue/11361. + if pos.IsKnown() { + msg = fmt.Sprintf("%v: %s", FmtPos(pos), msg) + } + errorMsgs = append(errorMsgs, errorMsg{ + pos: pos, + msg: msg + "\n", + }) +} + +// FmtPos formats pos as a file:line string. +func FmtPos(pos src.XPos) string { + if Ctxt == nil { + return "???" + } + return Ctxt.OutermostPos(pos).Format(Flag.C == 0, Flag.L == 1) +} + +// byPos sorts errors by source position. +type byPos []errorMsg + +func (x byPos) Len() int { return len(x) } +func (x byPos) Less(i, j int) bool { return x[i].pos.Before(x[j].pos) } +func (x byPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +// FlushErrors sorts errors seen so far by line number, prints them to stdout, +// and empties the errors array. +func FlushErrors() { + Ctxt.Bso.Flush() + if len(errorMsgs) == 0 { + return + } + sort.Stable(byPos(errorMsgs)) + for i, err := range errorMsgs { + if i == 0 || err.msg != errorMsgs[i-1].msg { + fmt.Printf("%s", err.msg) + } + } + errorMsgs = errorMsgs[:0] +} + +// lasterror keeps track of the most recently issued error, +// to avoid printing multiple error messages on the same line. +var lasterror struct { + syntax src.XPos // source position of last syntax error + other src.XPos // source position of last non-syntax error + msg string // error message of last non-syntax error +} + +// sameline reports whether two positions a, b are on the same line. +func sameline(a, b src.XPos) bool { + p := Ctxt.PosTable.Pos(a) + q := Ctxt.PosTable.Pos(b) + return p.Base() == q.Base() && p.Line() == q.Line() +} + +// Errorf reports a formatted error at the current line. +func Errorf(format string, args ...interface{}) { + ErrorfAt(Pos, format, args...) +} + +// ErrorfAt reports a formatted error message at pos. +func ErrorfAt(pos src.XPos, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + + if strings.HasPrefix(msg, "syntax error") { + numSyntaxErrors++ + // only one syntax error per line, no matter what error + if sameline(lasterror.syntax, pos) { + return + } + lasterror.syntax = pos + } else { + // only one of multiple equal non-syntax errors per line + // (flusherrors shows only one of them, so we filter them + // here as best as we can (they may not appear in order) + // so that we don't count them here and exit early, and + // then have nothing to show for.) + if sameline(lasterror.other, pos) && lasterror.msg == msg { + return + } + lasterror.other = pos + lasterror.msg = msg + } + + addErrorMsg(pos, "%s", msg) + numErrors++ + + hcrash() + if numErrors >= 10 && Flag.LowerE == 0 { + FlushErrors() + fmt.Printf("%v: too many errors\n", FmtPos(pos)) + ErrorExit() + } +} + +// ErrorfVers reports that a language feature (format, args) requires a later version of Go. +func ErrorfVers(lang string, format string, args ...interface{}) { + Errorf("%s requires %s or later (-lang was set to %s; check go.mod)", fmt.Sprintf(format, args...), lang, Flag.Lang) +} + +// UpdateErrorDot is a clumsy hack that rewrites the last error, +// if it was "LINE: undefined: NAME", to be "LINE: undefined: NAME in EXPR". +// It is used to give better error messages for dot (selector) expressions. +func UpdateErrorDot(line string, name, expr string) { + if len(errorMsgs) == 0 { + return + } + e := &errorMsgs[len(errorMsgs)-1] + if strings.HasPrefix(e.msg, line) && e.msg == fmt.Sprintf("%v: undefined: %v\n", line, name) { + e.msg = fmt.Sprintf("%v: undefined: %v in %v\n", line, name, expr) + } +} + +// Warnf reports a formatted warning at the current line. +// In general the Go compiler does NOT generate warnings, +// so this should be used only when the user has opted in +// to additional output by setting a particular flag. +func Warn(format string, args ...interface{}) { + WarnfAt(Pos, format, args...) +} + +// WarnfAt reports a formatted warning at pos. +// In general the Go compiler does NOT generate warnings, +// so this should be used only when the user has opted in +// to additional output by setting a particular flag. +func WarnfAt(pos src.XPos, format string, args ...interface{}) { + addErrorMsg(pos, format, args...) + if Flag.LowerM != 0 { + FlushErrors() + } +} + +// Fatalf reports a fatal error - an internal problem - at the current line and exits. +// If other errors have already been printed, then Fatalf just quietly exits. +// (The internal problem may have been caused by incomplete information +// after the already-reported errors, so best to let users fix those and +// try again without being bothered about a spurious internal error.) +// +// But if no errors have been printed, or if -d panic has been specified, +// Fatalf prints the error as an "internal compiler error". In a released build, +// it prints an error asking to file a bug report. In development builds, it +// prints a stack trace. +// +// If -h has been specified, Fatalf panics to force the usual runtime info dump. +func Fatalf(format string, args ...interface{}) { + FatalfAt(Pos, format, args...) +} + +// FatalfAt reports a fatal error - an internal problem - at pos and exits. +// If other errors have already been printed, then FatalfAt just quietly exits. +// (The internal problem may have been caused by incomplete information +// after the already-reported errors, so best to let users fix those and +// try again without being bothered about a spurious internal error.) +// +// But if no errors have been printed, or if -d panic has been specified, +// FatalfAt prints the error as an "internal compiler error". In a released build, +// it prints an error asking to file a bug report. In development builds, it +// prints a stack trace. +// +// If -h has been specified, FatalfAt panics to force the usual runtime info dump. +func FatalfAt(pos src.XPos, format string, args ...interface{}) { + FlushErrors() + + if Debug.Panic != 0 || numErrors == 0 { + fmt.Printf("%v: internal compiler error: ", FmtPos(pos)) + fmt.Printf(format, args...) + fmt.Printf("\n") + + // If this is a released compiler version, ask for a bug report. + if strings.HasPrefix(objabi.Version, "go") { + fmt.Printf("\n") + fmt.Printf("Please file a bug report including a short program that triggers the error.\n") + fmt.Printf("https://golang.org/issue/new\n") + } else { + // Not a release; dump a stack trace, too. + fmt.Println() + os.Stdout.Write(debug.Stack()) + fmt.Println() + } + } + + hcrash() + ErrorExit() +} + +// hcrash crashes the compiler when -h is set, to find out where a message is generated. +func hcrash() { + if Flag.LowerH != 0 { + FlushErrors() + if Flag.LowerO != "" { + os.Remove(Flag.LowerO) + } + panic("-h") + } +} + +// ErrorExit handles an error-status exit. +// It flushes any pending errors, removes the output file, and exits. +func ErrorExit() { + FlushErrors() + if Flag.LowerO != "" { + os.Remove(Flag.LowerO) + } + os.Exit(2) +} + +// ExitIfErrors calls ErrorExit if any errors have been reported. +func ExitIfErrors() { + if Errors() > 0 { + ErrorExit() + } +} |