summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/gc/main.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
commit73df946d56c74384511a194dd01dbe099584fd1a (patch)
treefd0bcea490dd81327ddfbb31e215439672c9a068 /src/cmd/compile/internal/gc/main.go
parentInitial commit. (diff)
downloadgolang-1.16-upstream.tar.xz
golang-1.16-upstream.zip
Adding upstream version 1.16.10.upstream/1.16.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/compile/internal/gc/main.go')
-rw-r--r--src/cmd/compile/internal/gc/main.go1610
1 files changed, 1610 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
new file mode 100644
index 0000000..a6963a3
--- /dev/null
+++ b/src/cmd/compile/internal/gc/main.go
@@ -0,0 +1,1610 @@
+// 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.
+
+//go:generate go run mkbuiltin.go
+
+package gc
+
+import (
+ "bufio"
+ "bytes"
+ "cmd/compile/internal/logopt"
+ "cmd/compile/internal/ssa"
+ "cmd/compile/internal/types"
+ "cmd/internal/bio"
+ "cmd/internal/dwarf"
+ "cmd/internal/goobj"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+ "cmd/internal/sys"
+ "flag"
+ "fmt"
+ "internal/goversion"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+ "regexp"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+var (
+ buildid string
+ spectre string
+ spectreIndex bool
+)
+
+var (
+ Debug_append int
+ Debug_checkptr int
+ Debug_closure int
+ Debug_compilelater int
+ debug_dclstack int
+ Debug_dumpptrs int
+ Debug_libfuzzer int
+ Debug_panic int
+ Debug_slice int
+ Debug_vlog bool
+ Debug_wb int
+ Debug_pctab string
+ Debug_locationlist int
+ Debug_typecheckinl int
+ Debug_gendwarfinl int
+ Debug_softfloat int
+ Debug_defer int
+)
+
+// Debug arguments.
+// These can be specified with the -d flag, as in "-d nil"
+// to set the debug_checknil variable.
+// Multiple options can be comma-separated.
+// Each option accepts an optional argument, as in "gcprog=2"
+var debugtab = []struct {
+ name string
+ help string
+ val interface{} // must be *int or *string
+}{
+ {"append", "print information about append compilation", &Debug_append},
+ {"checkptr", "instrument unsafe pointer conversions", &Debug_checkptr},
+ {"closure", "print information about closure compilation", &Debug_closure},
+ {"compilelater", "compile functions as late as possible", &Debug_compilelater},
+ {"disablenil", "disable nil checks", &disable_checknil},
+ {"dclstack", "run internal dclstack check", &debug_dclstack},
+ {"dumpptrs", "show Node pointer values in Dump/dumplist output", &Debug_dumpptrs},
+ {"gcprog", "print dump of GC programs", &Debug_gcprog},
+ {"libfuzzer", "coverage instrumentation for libfuzzer", &Debug_libfuzzer},
+ {"nil", "print information about nil checks", &Debug_checknil},
+ {"panic", "do not hide any compiler panic", &Debug_panic},
+ {"slice", "print information about slice compilation", &Debug_slice},
+ {"typeassert", "print information about type assertion inlining", &Debug_typeassert},
+ {"wb", "print information about write barriers", &Debug_wb},
+ {"export", "print export data", &Debug_export},
+ {"pctab", "print named pc-value table", &Debug_pctab},
+ {"locationlists", "print information about DWARF location list creation", &Debug_locationlist},
+ {"typecheckinl", "eager typechecking of inline function bodies", &Debug_typecheckinl},
+ {"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl},
+ {"softfloat", "force compiler to emit soft-float code", &Debug_softfloat},
+ {"defer", "print information about defer compilation", &Debug_defer},
+ {"fieldtrack", "enable fieldtracking", &objabi.Fieldtrack_enabled},
+}
+
+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"
+`
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
+ objabi.Flagprint(os.Stderr)
+ Exit(2)
+}
+
+func hidePanic() {
+ if Debug_panic == 0 && nsavederrors+nerrors > 0 {
+ // If we've already complained about things
+ // in the program, don't bother complaining
+ // about a panic too; let the user clean up
+ // the code and try again.
+ if err := recover(); err != nil {
+ errorexit()
+ }
+ }
+}
+
+// supportsDynlink reports whether or not the code generator for the given
+// architecture supports the -shared and -dynlink flags.
+func supportsDynlink(arch *sys.Arch) bool {
+ return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X)
+}
+
+// timing data for compiler phases
+var timings Timings
+var benchfile string
+
+var nowritebarrierrecCheck *nowritebarrierrecChecker
+
+// Main parses flags and Go source files specified in the command-line
+// arguments, type-checks the parsed Go package, compiles functions to machine
+// code, and finally writes the compiled package definition to disk.
+func Main(archInit func(*Arch)) {
+ timings.Start("fe", "init")
+
+ defer hidePanic()
+
+ archInit(&thearch)
+
+ Ctxt = obj.Linknew(thearch.LinkArch)
+ Ctxt.DiagFunc = yyerror
+ Ctxt.DiagFlush = flusherrors
+ Ctxt.Bso = bufio.NewWriter(os.Stdout)
+
+ // UseBASEntries is preferred because it shaves about 2% off build time, but LLDB, dsymutil, and dwarfdump
+ // on Darwin don't support it properly, especially since macOS 10.14 (Mojave). This is exposed as a flag
+ // to allow testing with LLVM tools on Linux, and to help with reporting this bug to the LLVM project.
+ // See bugs 31188 and 21945 (CLs 170638, 98075, 72371).
+ Ctxt.UseBASEntries = Ctxt.Headtype != objabi.Hdarwin
+
+ localpkg = types.NewPkg("", "")
+ localpkg.Prefix = "\"\""
+
+ // We won't know localpkg's height until after import
+ // processing. In the mean time, set to MaxPkgHeight to ensure
+ // height comparisons at least work until then.
+ localpkg.Height = types.MaxPkgHeight
+
+ // pseudo-package, for scoping
+ builtinpkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin?
+ builtinpkg.Prefix = "go.builtin" // not go%2ebuiltin
+
+ // pseudo-package, accessed by import "unsafe"
+ unsafepkg = types.NewPkg("unsafe", "unsafe")
+
+ // Pseudo-package that contains the compiler's builtin
+ // declarations for package runtime. These are declared in a
+ // separate package to avoid conflicts with package runtime's
+ // actual declarations, which may differ intentionally but
+ // insignificantly.
+ Runtimepkg = types.NewPkg("go.runtime", "runtime")
+ Runtimepkg.Prefix = "runtime"
+
+ // pseudo-packages used in symbol tables
+ itabpkg = types.NewPkg("go.itab", "go.itab")
+ itabpkg.Prefix = "go.itab" // not go%2eitab
+
+ itablinkpkg = types.NewPkg("go.itablink", "go.itablink")
+ itablinkpkg.Prefix = "go.itablink" // not go%2eitablink
+
+ trackpkg = types.NewPkg("go.track", "go.track")
+ trackpkg.Prefix = "go.track" // not go%2etrack
+
+ // pseudo-package used for map zero values
+ mappkg = types.NewPkg("go.map", "go.map")
+ mappkg.Prefix = "go.map"
+
+ // pseudo-package used for methods with anonymous receivers
+ gopkg = types.NewPkg("go", "")
+
+ Wasm := objabi.GOARCH == "wasm"
+
+ // Whether the limit for stack-allocated objects is much smaller than normal.
+ // This can be helpful for diagnosing certain causes of GC latency. See #27732.
+ smallFrames := false
+ jsonLogOpt := ""
+
+ flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
+ flag.BoolVar(&compiling_std, "std", false, "compiling standard library")
+ flag.StringVar(&localimport, "D", "", "set relative `path` for local imports")
+
+ objabi.Flagcount("%", "debug non-static initializers", &Debug.P)
+ objabi.Flagcount("B", "disable bounds checking", &Debug.B)
+ objabi.Flagcount("C", "disable printing of columns in error messages", &Debug.C)
+ objabi.Flagcount("E", "debug symbol export", &Debug.E)
+ objabi.Flagcount("K", "debug missing line numbers", &Debug.K)
+ objabi.Flagcount("L", "show full file names in error messages", &Debug.L)
+ objabi.Flagcount("N", "disable optimizations", &Debug.N)
+ objabi.Flagcount("S", "print assembly listing", &Debug.S)
+ objabi.Flagcount("W", "debug parse tree after type checking", &Debug.W)
+ objabi.Flagcount("e", "no limit on number of errors reported", &Debug.e)
+ objabi.Flagcount("h", "halt on error", &Debug.h)
+ objabi.Flagcount("j", "debug runtime-initialized variables", &Debug.j)
+ objabi.Flagcount("l", "disable inlining", &Debug.l)
+ objabi.Flagcount("m", "print optimization decisions", &Debug.m)
+ objabi.Flagcount("r", "debug generated wrappers", &Debug.r)
+ objabi.Flagcount("w", "debug type checking", &Debug.w)
+
+ objabi.Flagfn1("I", "add `directory` to import search path", addidir)
+ objabi.AddVersionFlag() // -V
+ flag.StringVar(&asmhdr, "asmhdr", "", "write assembly header to `file`")
+ flag.StringVar(&buildid, "buildid", "", "record `id` as the build id in the export metadata")
+ flag.IntVar(&nBackendWorkers, "c", 1, "concurrency during compilation, 1 means no concurrency")
+ flag.BoolVar(&pure_go, "complete", false, "compiling complete package (no C or assembly)")
+ flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`; try -d help")
+ flag.BoolVar(&flagDWARF, "dwarf", !Wasm, "generate DWARF symbols")
+ flag.BoolVar(&Ctxt.Flag_locationlists, "dwarflocationlists", true, "add location lists to DWARF in optimized mode")
+ flag.IntVar(&genDwarfInline, "gendwarfinl", 2, "generate DWARF inline info records")
+ objabi.Flagfn1("embedcfg", "read go:embed configuration from `file`", readEmbedCfg)
+ objabi.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap)
+ objabi.Flagfn1("importcfg", "read import configuration from `file`", readImportCfg)
+ flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`")
+ flag.StringVar(&flag_lang, "lang", "", "release to compile for")
+ flag.StringVar(&linkobj, "linkobj", "", "write linker-specific object to `file`")
+ objabi.Flagcount("live", "debug liveness analysis", &debuglive)
+ if sys.MSanSupported(objabi.GOOS, objabi.GOARCH) {
+ flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer")
+ }
+ flag.BoolVar(&nolocalimports, "nolocalimports", false, "reject local (relative) imports")
+ flag.StringVar(&outfile, "o", "", "write output to `file`")
+ flag.StringVar(&myimportpath, "p", "", "set expected package import `path`")
+ flag.BoolVar(&writearchive, "pack", false, "write to file.a instead of file.o")
+ if sys.RaceDetectorSupported(objabi.GOOS, objabi.GOARCH) {
+ flag.BoolVar(&flag_race, "race", false, "enable race detector")
+ }
+ flag.StringVar(&spectre, "spectre", spectre, "enable spectre mitigations in `list` (all, index, ret)")
+ if enableTrace {
+ flag.BoolVar(&trace, "t", false, "trace type-checking")
+ }
+ flag.StringVar(&pathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths")
+ flag.BoolVar(&Debug_vlog, "v", false, "increase debug verbosity")
+ flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier")
+ var flag_shared bool
+ var flag_dynlink bool
+ if supportsDynlink(thearch.LinkArch.Arch) {
+ flag.BoolVar(&flag_shared, "shared", false, "generate code that can be linked into a shared library")
+ flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries")
+ flag.BoolVar(&Ctxt.Flag_linkshared, "linkshared", false, "generate code that will be linked against Go shared libraries")
+ }
+ flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`")
+ flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`")
+ flag.Int64Var(&memprofilerate, "memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
+ var goversion string
+ flag.StringVar(&goversion, "goversion", "", "required version of the runtime")
+ var symabisPath string
+ flag.StringVar(&symabisPath, "symabis", "", "read symbol ABIs from `file`")
+ flag.StringVar(&traceprofile, "traceprofile", "", "write an execution trace to `file`")
+ flag.StringVar(&blockprofile, "blockprofile", "", "write block profile to `file`")
+ flag.StringVar(&mutexprofile, "mutexprofile", "", "write mutex profile to `file`")
+ flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
+ flag.BoolVar(&smallFrames, "smallframes", false, "reduce the size limit for stack allocated objects")
+ flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF")
+ flag.StringVar(&jsonLogOpt, "json", "", "version,destination for JSON compiler/optimizer logging")
+
+ objabi.Flagparse(usage)
+
+ Ctxt.Pkgpath = myimportpath
+
+ for _, f := range strings.Split(spectre, ",") {
+ f = strings.TrimSpace(f)
+ switch f {
+ default:
+ log.Fatalf("unknown setting -spectre=%s", f)
+ case "":
+ // nothing
+ case "all":
+ spectreIndex = true
+ Ctxt.Retpoline = true
+ case "index":
+ spectreIndex = true
+ case "ret":
+ Ctxt.Retpoline = true
+ }
+ }
+
+ if spectreIndex {
+ switch objabi.GOARCH {
+ case "amd64":
+ // ok
+ default:
+ log.Fatalf("GOARCH=%s does not support -spectre=index", objabi.GOARCH)
+ }
+ }
+
+ // Record flags that affect the build result. (And don't
+ // record flags that don't, since that would cause spurious
+ // changes in the binary.)
+ recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
+
+ if smallFrames {
+ maxStackVarSize = 128 * 1024
+ maxImplicitStackVarSize = 16 * 1024
+ }
+
+ Ctxt.Flag_shared = flag_dynlink || flag_shared
+ Ctxt.Flag_dynlink = flag_dynlink
+ Ctxt.Flag_optimize = Debug.N == 0
+
+ Ctxt.Debugasm = Debug.S
+ Ctxt.Debugvlog = Debug_vlog
+ if flagDWARF {
+ Ctxt.DebugInfo = debuginfo
+ Ctxt.GenAbstractFunc = genAbstractFunc
+ Ctxt.DwFixups = obj.NewDwarfFixupTable(Ctxt)
+ } else {
+ // turn off inline generation if no dwarf at all
+ genDwarfInline = 0
+ Ctxt.Flag_locationlists = false
+ }
+
+ if flag.NArg() < 1 && debugstr != "help" && debugstr != "ssa/help" {
+ usage()
+ }
+
+ if goversion != "" && goversion != runtime.Version() {
+ fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), goversion)
+ Exit(2)
+ }
+
+ checkLang()
+
+ if symabisPath != "" {
+ readSymABIs(symabisPath, myimportpath)
+ }
+
+ thearch.LinkArch.Init(Ctxt)
+
+ if outfile == "" {
+ 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 writearchive {
+ suffix = ".a"
+ }
+ outfile = p + suffix
+ }
+
+ startProfile()
+
+ 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 ispkgin(omit_pkgs) {
+ flag_race = false
+ flag_msan = false
+ }
+ if flag_race {
+ racepkg = types.NewPkg("runtime/race", "")
+ }
+ if flag_msan {
+ msanpkg = types.NewPkg("runtime/msan", "")
+ }
+ if flag_race || flag_msan {
+ instrumenting = true
+ }
+
+ if compiling_runtime && Debug.N != 0 {
+ log.Fatal("cannot disable optimizations while compiling runtime")
+ }
+ if nBackendWorkers < 1 {
+ log.Fatalf("-c must be at least 1, got %d", nBackendWorkers)
+ }
+ if nBackendWorkers > 1 && !concurrentBackendAllowed() {
+ log.Fatalf("cannot use concurrent backend compilation with provided flags; invoked as %v", os.Args)
+ }
+ if Ctxt.Flag_locationlists && len(Ctxt.Arch.DWARFRegisters) == 0 {
+ log.Fatalf("location lists requested but register mapping not available on %v", Ctxt.Arch.Name)
+ }
+
+ // parse -d argument
+ if debugstr != "" {
+ 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 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 := ssa.PhaseOption(phase, flag, val, valstring)
+ if err != "" {
+ log.Fatalf(err)
+ }
+ continue Split
+ }
+ log.Fatalf("unknown debug key -d %s\n", name)
+ }
+ }
+
+ if compiling_runtime {
+ // 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
+ if flagDWARF {
+ dwarf.EnableLogging(Debug_gendwarfinl != 0)
+ }
+
+ if Debug_softfloat != 0 {
+ thearch.SoftFloat = true
+ }
+
+ // enable inlining. for now:
+ // default: inlining on. (Debug.l == 1)
+ // -l: inlining off (Debug.l == 0)
+ // -l=2, -l=3: inlining on again, with extra debugging (Debug.l > 1)
+ if Debug.l <= 1 {
+ Debug.l = 1 - Debug.l
+ }
+
+ if jsonLogOpt != "" { // parse version,destination from json logging optimization.
+ logopt.LogJsonOption(jsonLogOpt)
+ }
+
+ ssaDump = os.Getenv("GOSSAFUNC")
+ ssaDir = os.Getenv("GOSSADIR")
+ if ssaDump != "" {
+ if strings.HasSuffix(ssaDump, "+") {
+ ssaDump = ssaDump[:len(ssaDump)-1]
+ ssaDumpStdout = true
+ }
+ spl := strings.Split(ssaDump, ":")
+ if len(spl) > 1 {
+ ssaDump = spl[0]
+ ssaDumpCFG = spl[1]
+ }
+ }
+
+ trackScopes = flagDWARF
+
+ Widthptr = thearch.LinkArch.PtrSize
+ Widthreg = thearch.LinkArch.RegSize
+
+ // initialize types package
+ // (we need to do this to break dependencies that otherwise
+ // would lead to import cycles)
+ types.Widthptr = Widthptr
+ types.Dowidth = dowidth
+ types.Fatalf = Fatalf
+ types.Sconv = func(s *types.Sym, flag, mode int) string {
+ return sconv(s, FmtFlag(flag), fmtMode(mode))
+ }
+ types.Tconv = func(t *types.Type, flag, mode int) string {
+ return tconv(t, FmtFlag(flag), fmtMode(mode))
+ }
+ types.FormatSym = func(sym *types.Sym, s fmt.State, verb rune, mode int) {
+ symFormat(sym, s, verb, fmtMode(mode))
+ }
+ types.FormatType = func(t *types.Type, s fmt.State, verb rune, mode int) {
+ typeFormat(t, s, verb, fmtMode(mode))
+ }
+ types.TypeLinkSym = func(t *types.Type) *obj.LSym {
+ return typenamesym(t).Linksym()
+ }
+ types.FmtLeft = int(FmtLeft)
+ types.FmtUnsigned = int(FmtUnsigned)
+ types.FErr = int(FErr)
+ types.Ctxt = Ctxt
+
+ initUniverse()
+
+ dclcontext = PEXTERN
+ nerrors = 0
+
+ autogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
+
+ timings.Start("fe", "loadsys")
+ loadsys()
+
+ timings.Start("fe", "parse")
+ lines := parseFiles(flag.Args())
+ timings.Stop()
+ timings.AddEvent(int64(lines), "lines")
+
+ finishUniverse()
+
+ recordPackageName()
+
+ typecheckok = true
+
+ // Process top-level declarations in phases.
+
+ // Phase 1: const, type, and names and types of funcs.
+ // This will gather all the information about types
+ // and methods but doesn't depend on any of it.
+ //
+ // We also defer type alias declarations until phase 2
+ // to avoid cycles like #18640.
+ // TODO(gri) Remove this again once we have a fix for #25838.
+
+ // Don't use range--typecheck can add closures to xtop.
+ timings.Start("fe", "typecheck", "top1")
+ for i := 0; i < len(xtop); i++ {
+ n := xtop[i]
+ if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias()) {
+ xtop[i] = typecheck(n, ctxStmt)
+ }
+ }
+
+ // Phase 2: Variable assignments.
+ // To check interface assignments, depends on phase 1.
+
+ // Don't use range--typecheck can add closures to xtop.
+ timings.Start("fe", "typecheck", "top2")
+ for i := 0; i < len(xtop); i++ {
+ n := xtop[i]
+ if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias() {
+ xtop[i] = typecheck(n, ctxStmt)
+ }
+ }
+
+ // Phase 3: Type check function bodies.
+ // Don't use range--typecheck can add closures to xtop.
+ timings.Start("fe", "typecheck", "func")
+ var fcount int64
+ for i := 0; i < len(xtop); i++ {
+ n := xtop[i]
+ if n.Op == ODCLFUNC {
+ Curfn = n
+ decldepth = 1
+ saveerrors()
+ typecheckslice(Curfn.Nbody.Slice(), ctxStmt)
+ checkreturn(Curfn)
+ if nerrors != 0 {
+ Curfn.Nbody.Set(nil) // type errors; do not compile
+ }
+ // Now that we've checked whether n terminates,
+ // we can eliminate some obviously dead code.
+ deadcode(Curfn)
+ fcount++
+ }
+ }
+ // With all types checked, it's now safe to verify map keys. One single
+ // check past phase 9 isn't sufficient, as we may exit with other errors
+ // before then, thus skipping map key errors.
+ checkMapKeys()
+ timings.AddEvent(fcount, "funcs")
+
+ if nsavederrors+nerrors != 0 {
+ errorexit()
+ }
+
+ fninit(xtop)
+
+ // Phase 4: Decide how to capture closed variables.
+ // This needs to run before escape analysis,
+ // because variables captured by value do not escape.
+ timings.Start("fe", "capturevars")
+ for _, n := range xtop {
+ if n.Op == ODCLFUNC && n.Func.Closure != nil {
+ Curfn = n
+ capturevars(n)
+ }
+ }
+ capturevarscomplete = true
+
+ Curfn = nil
+
+ if nsavederrors+nerrors != 0 {
+ errorexit()
+ }
+
+ // Phase 5: Inlining
+ timings.Start("fe", "inlining")
+ if Debug_typecheckinl != 0 {
+ // Typecheck imported function bodies if Debug.l > 1,
+ // otherwise lazily when used or re-exported.
+ for _, n := range importlist {
+ if n.Func.Inl != nil {
+ saveerrors()
+ typecheckinl(n)
+ }
+ }
+
+ if nsavederrors+nerrors != 0 {
+ errorexit()
+ }
+ }
+
+ if Debug.l != 0 {
+ // Find functions that can be inlined and clone them before walk expands them.
+ visitBottomUp(xtop, func(list []*Node, recursive bool) {
+ numfns := numNonClosures(list)
+ for _, n := range list {
+ if !recursive || numfns > 1 {
+ // We allow inlining if there is no
+ // recursion, or the recursion cycle is
+ // across more than one function.
+ caninl(n)
+ } else {
+ if Debug.m > 1 {
+ fmt.Printf("%v: cannot inline %v: recursive\n", n.Line(), n.Func.Nname)
+ }
+ }
+ inlcalls(n)
+ }
+ })
+ }
+
+ for _, n := range xtop {
+ if n.Op == ODCLFUNC {
+ devirtualize(n)
+ }
+ }
+ Curfn = nil
+
+ // Phase 6: Escape analysis.
+ // Required for moving heap allocations onto stack,
+ // which in turn is required by the closure implementation,
+ // which stores the addresses of stack variables into the closure.
+ // If the closure does not escape, it needs to be on the stack
+ // or else the stack copier will not update it.
+ // Large values are also moved off stack in escape analysis;
+ // because large values may contain pointers, it must happen early.
+ timings.Start("fe", "escapes")
+ escapes(xtop)
+
+ // Collect information for go:nowritebarrierrec
+ // checking. This must happen before transformclosure.
+ // We'll do the final check after write barriers are
+ // inserted.
+ if compiling_runtime {
+ nowritebarrierrecCheck = newNowritebarrierrecChecker()
+ }
+
+ // Phase 7: Transform closure bodies to properly reference captured variables.
+ // This needs to happen before walk, because closures must be transformed
+ // before walk reaches a call of a closure.
+ timings.Start("fe", "xclosures")
+ for _, n := range xtop {
+ if n.Op == ODCLFUNC && n.Func.Closure != nil {
+ Curfn = n
+ transformclosure(n)
+ }
+ }
+
+ // Prepare for SSA compilation.
+ // This must be before peekitabs, because peekitabs
+ // can trigger function compilation.
+ initssaconfig()
+
+ // Just before compilation, compile itabs found on
+ // the right side of OCONVIFACE so that methods
+ // can be de-virtualized during compilation.
+ Curfn = nil
+ peekitabs()
+
+ // Phase 8: Compile top level functions.
+ // Don't use range--walk can add functions to xtop.
+ timings.Start("be", "compilefuncs")
+ fcount = 0
+ for i := 0; i < len(xtop); i++ {
+ n := xtop[i]
+ if n.Op == ODCLFUNC {
+ funccompile(n)
+ fcount++
+ }
+ }
+ timings.AddEvent(fcount, "funcs")
+
+ compileFunctions()
+
+ if nowritebarrierrecCheck != nil {
+ // Write barriers are now known. Check the
+ // call graph.
+ nowritebarrierrecCheck.check()
+ nowritebarrierrecCheck = nil
+ }
+
+ // Finalize DWARF inline routine DIEs, then explicitly turn off
+ // DWARF inlining gen so as to avoid problems with generated
+ // method wrappers.
+ if Ctxt.DwFixups != nil {
+ Ctxt.DwFixups.Finalize(myimportpath, Debug_gendwarfinl != 0)
+ Ctxt.DwFixups = nil
+ genDwarfInline = 0
+ }
+
+ // Phase 9: Check external declarations.
+ timings.Start("be", "externaldcls")
+ for i, n := range externdcl {
+ if n.Op == ONAME {
+ externdcl[i] = typecheck(externdcl[i], ctxExpr)
+ }
+ }
+ // Check the map keys again, since we typechecked the external
+ // declarations.
+ checkMapKeys()
+
+ if nerrors+nsavederrors != 0 {
+ errorexit()
+ }
+
+ // Write object data to disk.
+ timings.Start("be", "dumpobj")
+ dumpdata()
+ Ctxt.NumberSyms()
+ dumpobj()
+ if asmhdr != "" {
+ dumpasmhdr()
+ }
+
+ // Check whether any of the functions we have compiled have gigantic stack frames.
+ sort.Slice(largeStackFrames, func(i, j int) bool {
+ return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
+ })
+ for _, large := range largeStackFrames {
+ if large.callee != 0 {
+ yyerrorl(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
+ } else {
+ yyerrorl(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
+ }
+ }
+
+ if len(funcStack) != 0 {
+ Fatalf("funcStack is non-empty: %v", len(funcStack))
+ }
+ if len(compilequeue) != 0 {
+ Fatalf("%d uncompiled functions", len(compilequeue))
+ }
+
+ logopt.FlushLoggedOpts(Ctxt, myimportpath)
+
+ if nerrors+nsavederrors != 0 {
+ errorexit()
+ }
+
+ flusherrors()
+ timings.Stop()
+
+ if benchfile != "" {
+ if err := writebench(benchfile); err != nil {
+ log.Fatalf("cannot write benchmark data: %v", err)
+ }
+ }
+}
+
+// numNonClosures returns the number of functions in list which are not closures.
+func numNonClosures(list []*Node) int {
+ count := 0
+ for _, n := range list {
+ if n.Func.Closure == nil {
+ count++
+ }
+ }
+ return count
+}
+
+func writebench(filename string) error {
+ f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
+ if err != nil {
+ return err
+ }
+
+ var buf bytes.Buffer
+ fmt.Fprintln(&buf, "commit:", objabi.Version)
+ fmt.Fprintln(&buf, "goos:", runtime.GOOS)
+ fmt.Fprintln(&buf, "goarch:", runtime.GOARCH)
+ timings.Write(&buf, "BenchmarkCompile:"+myimportpath+":")
+
+ n, err := f.Write(buf.Bytes())
+ if err != nil {
+ return err
+ }
+ if n != buf.Len() {
+ panic("bad writer")
+ }
+
+ return f.Close()
+}
+
+var (
+ importMap = map[string]string{}
+ packageFile map[string]string // nil means not in use
+)
+
+func addImportMap(s 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")
+ }
+ importMap[source] = actual
+}
+
+func readImportCfg(file string) {
+ 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)
+ }
+ importMap[before] = after
+ case "packagefile":
+ if before == "" || after == "" {
+ log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
+ }
+ packageFile[before] = after
+ }
+ }
+}
+
+// symabiDefs and symabiRefs record the defined and referenced ABIs of
+// symbols required by non-Go code. These are keyed by link symbol
+// name, where the local package prefix is always `"".`
+var symabiDefs, symabiRefs map[string]obj.ABI
+
+// readSymABIs reads a symabis file that specifies definitions and
+// references of text symbols by ABI.
+//
+// The symabis format is a set of lines, where each line is a sequence
+// of whitespace-separated fields. The first field is a verb and is
+// either "def" for defining a symbol ABI or "ref" for referencing a
+// symbol using an ABI. For both "def" and "ref", the second field is
+// the symbol name and the third field is the ABI name, as one of the
+// named cmd/internal/obj.ABI constants.
+func readSymABIs(file, myimportpath string) {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ log.Fatalf("-symabis: %v", err)
+ }
+
+ symabiDefs = make(map[string]obj.ABI)
+ symabiRefs = make(map[string]obj.ABI)
+
+ localPrefix := ""
+ if myimportpath != "" {
+ // Symbols in this package may be written either as
+ // "".X or with the package's import path already in
+ // the symbol.
+ localPrefix = objabi.PathToPrefix(myimportpath) + "."
+ }
+
+ for lineNum, line := range strings.Split(string(data), "\n") {
+ lineNum++ // 1-based
+ line = strings.TrimSpace(line)
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+
+ parts := strings.Fields(line)
+ switch parts[0] {
+ case "def", "ref":
+ // Parse line.
+ if len(parts) != 3 {
+ log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
+ }
+ sym, abistr := parts[1], parts[2]
+ abi, valid := obj.ParseABI(abistr)
+ if !valid {
+ log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
+ }
+
+ // If the symbol is already prefixed with
+ // myimportpath, rewrite it to start with ""
+ // so it matches the compiler's internal
+ // symbol names.
+ if localPrefix != "" && strings.HasPrefix(sym, localPrefix) {
+ sym = `"".` + sym[len(localPrefix):]
+ }
+
+ // Record for later.
+ if parts[0] == "def" {
+ symabiDefs[sym] = abi
+ } else {
+ symabiRefs[sym] = abi
+ }
+ default:
+ log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
+ }
+ }
+}
+
+func saveerrors() {
+ nsavederrors += nerrors
+ nerrors = 0
+}
+
+func arsize(b *bufio.Reader, name string) int {
+ var buf [ArhdrSize]byte
+ if _, err := io.ReadFull(b, buf[:]); err != nil {
+ return -1
+ }
+ aname := strings.Trim(string(buf[0:16]), " ")
+ if !strings.HasPrefix(aname, name) {
+ return -1
+ }
+ asize := strings.Trim(string(buf[48:58]), " ")
+ i, _ := strconv.Atoi(asize)
+ return i
+}
+
+var idirs []string
+
+func addidir(dir string) {
+ if dir != "" {
+ idirs = append(idirs, dir)
+ }
+}
+
+func isDriveLetter(b byte) bool {
+ return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
+}
+
+// is this path a local name? begins with ./ or ../ or /
+func islocalname(name string) bool {
+ return strings.HasPrefix(name, "/") ||
+ runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' ||
+ strings.HasPrefix(name, "./") || name == "." ||
+ strings.HasPrefix(name, "../") || name == ".."
+}
+
+func findpkg(name string) (file string, ok bool) {
+ if islocalname(name) {
+ if nolocalimports {
+ return "", false
+ }
+
+ if packageFile != nil {
+ file, ok = packageFile[name]
+ return file, ok
+ }
+
+ // try .a before .6. important for building libraries:
+ // if there is an array.6 in the array.a library,
+ // want to find all of array.a, not just array.6.
+ file = fmt.Sprintf("%s.a", name)
+ if _, err := os.Stat(file); err == nil {
+ return file, true
+ }
+ file = fmt.Sprintf("%s.o", name)
+ if _, err := os.Stat(file); err == nil {
+ return file, true
+ }
+ return "", false
+ }
+
+ // local imports should be canonicalized already.
+ // don't want to see "encoding/../encoding/base64"
+ // as different from "encoding/base64".
+ if q := path.Clean(name); q != name {
+ yyerror("non-canonical import path %q (should be %q)", name, q)
+ return "", false
+ }
+
+ if packageFile != nil {
+ file, ok = packageFile[name]
+ return file, ok
+ }
+
+ for _, dir := range idirs {
+ file = fmt.Sprintf("%s/%s.a", dir, name)
+ if _, err := os.Stat(file); err == nil {
+ return file, true
+ }
+ file = fmt.Sprintf("%s/%s.o", dir, name)
+ if _, err := os.Stat(file); err == nil {
+ return file, true
+ }
+ }
+
+ if objabi.GOROOT != "" {
+ suffix := ""
+ suffixsep := ""
+ if flag_installsuffix != "" {
+ suffixsep = "_"
+ suffix = flag_installsuffix
+ } else if flag_race {
+ suffixsep = "_"
+ suffix = "race"
+ } else if flag_msan {
+ suffixsep = "_"
+ suffix = "msan"
+ }
+
+ file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffixsep, suffix, name)
+ if _, err := os.Stat(file); err == nil {
+ return file, true
+ }
+ file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffixsep, suffix, name)
+ if _, err := os.Stat(file); err == nil {
+ return file, true
+ }
+ }
+
+ return "", false
+}
+
+// loadsys loads the definitions for the low-level runtime functions,
+// so that the compiler can generate calls to them,
+// but does not make them visible to user code.
+func loadsys() {
+ types.Block = 1
+
+ inimport = true
+ typecheckok = true
+
+ typs := runtimeTypes()
+ for _, d := range &runtimeDecls {
+ sym := Runtimepkg.Lookup(d.name)
+ typ := typs[d.typ]
+ switch d.tag {
+ case funcTag:
+ importfunc(Runtimepkg, src.NoXPos, sym, typ)
+ case varTag:
+ importvar(Runtimepkg, src.NoXPos, sym, typ)
+ default:
+ Fatalf("unhandled declaration tag %v", d.tag)
+ }
+ }
+
+ typecheckok = false
+ inimport = false
+}
+
+// myheight tracks the local package's height based on packages
+// imported so far.
+var myheight int
+
+func importfile(f *Val) *types.Pkg {
+ path_, ok := f.U.(string)
+ if !ok {
+ yyerror("import path must be a string")
+ return nil
+ }
+
+ if len(path_) == 0 {
+ yyerror("import path is empty")
+ return nil
+ }
+
+ if isbadimport(path_, false) {
+ return nil
+ }
+
+ // The package name main is no longer reserved,
+ // but we reserve the import path "main" to identify
+ // the main package, just as we reserve the import
+ // path "math" to identify the standard math package.
+ if path_ == "main" {
+ yyerror("cannot import \"main\"")
+ errorexit()
+ }
+
+ if myimportpath != "" && path_ == myimportpath {
+ yyerror("import %q while compiling that package (import cycle)", path_)
+ errorexit()
+ }
+
+ if mapped, ok := importMap[path_]; ok {
+ path_ = mapped
+ }
+
+ if path_ == "unsafe" {
+ return unsafepkg
+ }
+
+ if islocalname(path_) {
+ if path_[0] == '/' {
+ yyerror("import path cannot be absolute path")
+ return nil
+ }
+
+ prefix := Ctxt.Pathname
+ if localimport != "" {
+ prefix = localimport
+ }
+ path_ = path.Join(prefix, path_)
+
+ if isbadimport(path_, true) {
+ return nil
+ }
+ }
+
+ file, found := findpkg(path_)
+ if !found {
+ yyerror("can't find import: %q", path_)
+ errorexit()
+ }
+
+ importpkg := types.NewPkg(path_, "")
+ if importpkg.Imported {
+ return importpkg
+ }
+
+ importpkg.Imported = true
+
+ imp, err := bio.Open(file)
+ if err != nil {
+ yyerror("can't open import: %q: %v", path_, err)
+ errorexit()
+ }
+ defer imp.Close()
+
+ // check object header
+ p, err := imp.ReadString('\n')
+ if err != nil {
+ yyerror("import %s: reading input: %v", file, err)
+ errorexit()
+ }
+
+ if p == "!<arch>\n" { // package archive
+ // package export block should be first
+ sz := arsize(imp.Reader, "__.PKGDEF")
+ if sz <= 0 {
+ yyerror("import %s: not a package file", file)
+ errorexit()
+ }
+ p, err = imp.ReadString('\n')
+ if err != nil {
+ yyerror("import %s: reading input: %v", file, err)
+ errorexit()
+ }
+ }
+
+ if !strings.HasPrefix(p, "go object ") {
+ yyerror("import %s: not a go object file: %s", file, p)
+ errorexit()
+ }
+ q := fmt.Sprintf("%s %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version, objabi.Expstring())
+ if p[10:] != q {
+ yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q)
+ errorexit()
+ }
+
+ // process header lines
+ for {
+ p, err = imp.ReadString('\n')
+ if err != nil {
+ yyerror("import %s: reading input: %v", file, err)
+ errorexit()
+ }
+ if p == "\n" {
+ break // header ends with blank line
+ }
+ }
+
+ // In the importfile, if we find:
+ // $$\n (textual format): not supported anymore
+ // $$B\n (binary format) : import directly, then feed the lexer a dummy statement
+
+ // look for $$
+ var c byte
+ for {
+ c, err = imp.ReadByte()
+ if err != nil {
+ break
+ }
+ if c == '$' {
+ c, err = imp.ReadByte()
+ if c == '$' || err != nil {
+ break
+ }
+ }
+ }
+
+ // get character after $$
+ if err == nil {
+ c, _ = imp.ReadByte()
+ }
+
+ var fingerprint goobj.FingerprintType
+ switch c {
+ case '\n':
+ yyerror("cannot import %s: old export format no longer supported (recompile library)", path_)
+ return nil
+
+ case 'B':
+ if Debug_export != 0 {
+ fmt.Printf("importing %s (%s)\n", path_, file)
+ }
+ imp.ReadByte() // skip \n after $$B
+
+ c, err = imp.ReadByte()
+ if err != nil {
+ yyerror("import %s: reading input: %v", file, err)
+ errorexit()
+ }
+
+ // Indexed format is distinguished by an 'i' byte,
+ // whereas previous export formats started with 'c', 'd', or 'v'.
+ if c != 'i' {
+ yyerror("import %s: unexpected package format byte: %v", file, c)
+ errorexit()
+ }
+ fingerprint = iimport(importpkg, imp)
+
+ default:
+ yyerror("no import in %q", path_)
+ errorexit()
+ }
+
+ // assume files move (get installed) so don't record the full path
+ if packageFile != nil {
+ // If using a packageFile map, assume path_ can be recorded directly.
+ Ctxt.AddImport(path_, fingerprint)
+ } else {
+ // For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a".
+ Ctxt.AddImport(file[len(file)-len(path_)-len(".a"):], fingerprint)
+ }
+
+ if importpkg.Height >= myheight {
+ myheight = importpkg.Height + 1
+ }
+
+ return importpkg
+}
+
+func pkgnotused(lineno src.XPos, path string, name string) {
+ // If the package was imported with a name other than the final
+ // import path element, show it explicitly in the error message.
+ // Note that this handles both renamed imports and imports of
+ // packages containing unconventional package declarations.
+ // Note that this uses / always, even on Windows, because Go import
+ // paths always use forward slashes.
+ elem := path
+ if i := strings.LastIndex(elem, "/"); i >= 0 {
+ elem = elem[i+1:]
+ }
+ if name == "" || elem == name {
+ yyerrorl(lineno, "imported and not used: %q", path)
+ } else {
+ yyerrorl(lineno, "imported and not used: %q as %s", path, name)
+ }
+}
+
+func mkpackage(pkgname string) {
+ if localpkg.Name == "" {
+ if pkgname == "_" {
+ yyerror("invalid package name _")
+ }
+ localpkg.Name = pkgname
+ } else {
+ if pkgname != localpkg.Name {
+ yyerror("package %s; expected %s", pkgname, localpkg.Name)
+ }
+ }
+}
+
+func clearImports() {
+ type importedPkg struct {
+ pos src.XPos
+ path string
+ name string
+ }
+ var unused []importedPkg
+
+ for _, s := range localpkg.Syms {
+ n := asNode(s.Def)
+ if n == nil {
+ continue
+ }
+ if n.Op == OPACK {
+ // throw away top-level package name left over
+ // from previous file.
+ // leave s->block set to cause redeclaration
+ // errors if a conflicting top-level name is
+ // introduced by a different file.
+ if !n.Name.Used() && nsyntaxerrors == 0 {
+ unused = append(unused, importedPkg{n.Pos, n.Name.Pkg.Path, s.Name})
+ }
+ s.Def = nil
+ continue
+ }
+ if IsAlias(s) {
+ // throw away top-level name left over
+ // from previous import . "x"
+ if n.Name != nil && n.Name.Pack != nil && !n.Name.Pack.Name.Used() && nsyntaxerrors == 0 {
+ unused = append(unused, importedPkg{n.Name.Pack.Pos, n.Name.Pack.Name.Pkg.Path, ""})
+ n.Name.Pack.Name.SetUsed(true)
+ }
+ s.Def = nil
+ continue
+ }
+ }
+
+ sort.Slice(unused, func(i, j int) bool { return unused[i].pos.Before(unused[j].pos) })
+ for _, pkg := range unused {
+ pkgnotused(pkg.pos, pkg.path, pkg.name)
+ }
+}
+
+func IsAlias(sym *types.Sym) bool {
+ return sym.Def != nil && asNode(sym.Def).Sym != sym
+}
+
+// By default, assume any debug flags are incompatible with concurrent
+// compilation. A few are safe and potentially in common use for
+// normal compiles, though; return true for those.
+func concurrentFlagOk() bool {
+ // Report whether any debug flag that would prevent concurrent
+ // compilation is set, by zeroing out the allowed ones and then
+ // checking if the resulting struct is zero.
+ d := Debug
+ d.B = 0 // disable bounds checking
+ d.C = 0 // disable printing of columns in error messages
+ d.e = 0 // no limit on errors; errors all come from non-concurrent code
+ d.N = 0 // disable optimizations
+ d.l = 0 // disable inlining
+ d.w = 0 // all printing happens before compilation
+ d.W = 0 // all printing happens before compilation
+ d.S = 0 // printing disassembly happens at the end (but see concurrentBackendAllowed below)
+
+ return d == DebugFlags{}
+}
+
+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 Debug_vlog || debugstr != "" || debuglive > 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
+}
+
+// recordFlags records the specified command-line flags to be placed
+// in the DWARF info.
+func recordFlags(flags ...string) {
+ if myimportpath == "" {
+ // We can't record the flags if we don't know what the
+ // package name is.
+ return
+ }
+
+ type BoolFlag interface {
+ IsBoolFlag() bool
+ }
+ type CountFlag interface {
+ IsCountFlag() bool
+ }
+ var cmd bytes.Buffer
+ for _, name := range flags {
+ f := flag.Lookup(name)
+ if f == nil {
+ continue
+ }
+ getter := f.Value.(flag.Getter)
+ if getter.String() == f.DefValue {
+ // Flag has default value, so omit it.
+ continue
+ }
+ if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() {
+ val, ok := getter.Get().(bool)
+ if ok && val {
+ fmt.Fprintf(&cmd, " -%s", f.Name)
+ continue
+ }
+ }
+ if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() {
+ val, ok := getter.Get().(int)
+ if ok && val == 1 {
+ fmt.Fprintf(&cmd, " -%s", f.Name)
+ continue
+ }
+ }
+ fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get())
+ }
+
+ if cmd.Len() == 0 {
+ return
+ }
+ s := Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + myimportpath)
+ s.Type = objabi.SDWARFCUINFO
+ // Sometimes (for example when building tests) we can link
+ // together two package main archives. So allow dups.
+ s.Set(obj.AttrDuplicateOK, true)
+ Ctxt.Data = append(Ctxt.Data, s)
+ s.P = cmd.Bytes()[1:]
+}
+
+// recordPackageName records the name of the package being
+// compiled, so that the linker can save it in the compile unit's DIE.
+func recordPackageName() {
+ s := Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + myimportpath)
+ s.Type = objabi.SDWARFCUINFO
+ // Sometimes (for example when building tests) we can link
+ // together two package main archives. So allow dups.
+ s.Set(obj.AttrDuplicateOK, true)
+ Ctxt.Data = append(Ctxt.Data, s)
+ s.P = []byte(localpkg.Name)
+}
+
+// flag_lang is the language version we are compiling for, set by the -lang flag.
+var flag_lang string
+
+// currentLang returns the current language version.
+func currentLang() string {
+ return fmt.Sprintf("go1.%d", goversion.Version)
+}
+
+// goVersionRE is a regular expression that matches the valid
+// arguments to the -lang flag.
+var goVersionRE = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`)
+
+// A lang is a language version broken into major and minor numbers.
+type lang struct {
+ major, minor int
+}
+
+// langWant is the desired language version set by the -lang flag.
+// If the -lang flag is not set, this is the zero value, meaning that
+// any language version is supported.
+var langWant lang
+
+// langSupported reports whether language version major.minor is
+// supported in a particular package.
+func langSupported(major, minor int, pkg *types.Pkg) bool {
+ if pkg == nil {
+ // TODO(mdempsky): Set Pkg for local types earlier.
+ pkg = localpkg
+ }
+ if pkg != localpkg {
+ // Assume imported packages passed type-checking.
+ return true
+ }
+
+ if langWant.major == 0 && langWant.minor == 0 {
+ return true
+ }
+ return langWant.major > major || (langWant.major == major && langWant.minor >= minor)
+}
+
+// checkLang verifies that the -lang flag holds a valid value, and
+// exits if not. It initializes data used by langSupported.
+func checkLang() {
+ if flag_lang == "" {
+ return
+ }
+
+ var err error
+ langWant, err = parseLang(flag_lang)
+ if err != nil {
+ log.Fatalf("invalid value %q for -lang: %v", flag_lang, err)
+ }
+
+ if def := currentLang(); flag_lang != def {
+ defVers, err := parseLang(def)
+ if err != nil {
+ log.Fatalf("internal error parsing default lang %q: %v", def, err)
+ }
+ if langWant.major > defVers.major || (langWant.major == defVers.major && langWant.minor > defVers.minor) {
+ log.Fatalf("invalid value %q for -lang: max known version is %q", flag_lang, def)
+ }
+ }
+}
+
+// parseLang parses a -lang option into a langVer.
+func parseLang(s string) (lang, error) {
+ matches := goVersionRE.FindStringSubmatch(s)
+ if matches == nil {
+ return lang{}, fmt.Errorf(`should be something like "go1.12"`)
+ }
+ major, err := strconv.Atoi(matches[1])
+ if err != nil {
+ return lang{}, err
+ }
+ minor, err := strconv.Atoi(matches[2])
+ if err != nil {
+ return lang{}, err
+ }
+ return lang{major: major, minor: minor}, nil
+}