Skip to content
Snippets Groups Projects
main.go 44.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • // 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"
    
    	"cmd/compile/internal/ssa"
    
    	"cmd/internal/bio"
    
    Russ Cox's avatar
    Russ Cox committed
    	"sort"
    
    	"strconv"
    	"strings"
    )
    
    var imported_unsafe bool
    
    var (
    
    	buildid      string
    	spectre      string
    	spectreIndex bool
    
    	Debug_closure      int
    	Debug_compilelater int
    	debug_dclstack     int
    
    	Debug_panic        int
    	Debug_slice        int
    	Debug_vlog         bool
    	Debug_wb           int
    	Debug_pctab        string
    
    	Debug_typecheckinl 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
    
    	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},
    	{"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},
    
    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"
    `
    
    
    	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.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")
    
    
    	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("", "")
    
    	// 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")
    
    
    	// 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")
    
    	// 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
    
    	flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
    
    	flag.BoolVar(&compiling_std, "std", false, "compiling standard library")
    
    	objabi.Flagcount("%", "debug non-static initializers", &Debug['%'])
    	objabi.Flagcount("B", "disable bounds checking", &Debug['B'])
    	objabi.Flagcount("C", "disable printing of columns in error messages", &Debug['C']) // TODO(gri) remove eventually
    
    	flag.StringVar(&localimport, "D", "", "set relative `path` for local imports")
    
    	objabi.Flagcount("E", "debug symbol export", &Debug['E'])
    	objabi.Flagfn1("I", "add `directory` to import search path", addidir)
    	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'])
    
    	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.Flagcount("e", "no limit on number of errors reported", &Debug['e'])
    	objabi.Flagcount("h", "halt on error", &Debug['h'])
    	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`")
    
    	objabi.Flagcount("j", "debug runtime-initialized variables", &Debug['j'])
    	objabi.Flagcount("l", "disable inlining", &Debug['l'])
    
    	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)
    	objabi.Flagcount("m", "print optimization decisions", &Debug['m'])
    
    	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")
    
    	objabi.Flagcount("r", "debug generated wrappers", &Debug['r'])
    
    	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")
    
    	objabi.Flagcount("w", "debug type checking", &Debug['w'])
    
    	flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier")
    	var flag_shared 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")
    
    	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']
    
    	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" {
    
    	if goversion != "" && goversion != runtime.Version() {
    		fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), goversion)
    		Exit(2)
    	}
    
    
    	if symabisPath != "" {
    		readSymABIs(symabisPath, myimportpath)
    	}
    
    
    	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
    	}
    
    
    	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.
    
    	if ispkgin(omit_pkgs) {
    		flag_race = false
    		flag_msan = false
    	}
    
    		racepkg = types.NewPkg("runtime/race", "")
    
    		msanpkg = types.NewPkg("runtime/msan", "")
    
    	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)
    
    			val, valstring, haveInt := 1, "", true
    			if i := strings.IndexAny(name, "=:"); i >= 0 {
    
    				name, valstring = name[:i], name[i+1:]
    				val, err = strconv.Atoi(valstring)
    
    					val, haveInt = 1, false
    
    				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)
    		}
    	}
    
    
    		// Runtime can't use -d=checkptr, at least not yet.
    
    
    		// 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")
    
    	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 {
    
    	}
    	types.FmtLeft = int(FmtLeft)
    	types.FmtUnsigned = int(FmtUnsigned)
    
    	types.FErr = int(FErr)
    
    	autogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
    
    
    	timings.Start("fe", "loadsys")
    
    	timings.Start("fe", "parse")
    
    	lines := parseFiles(flag.Args())
    
    	timings.AddEvent(int64(lines), "lines")
    
    	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++ {
    
    		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++ {
    
    		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 op := n.Op; op == ODCLFUNC || op == OCLOSURE {
    			Curfn = n
    
    			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)
    
    Ainar Garipov's avatar
    Ainar Garipov committed
    	// 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.
    
    	timings.AddEvent(fcount, "funcs")
    
    	if nsavederrors+nerrors != 0 {
    		errorexit()
    	}
    
    
    	// 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)
    		}
    	}
    
    
    	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 {
    
    				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) {
    
    			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.
    
    				} else {
    					if Debug['m'] > 1 {
    						fmt.Printf("%v: cannot inline %v: recursive\n", n.Line(), n.Func.Nname)
    					}
    
    			}
    		})
    	}
    
    	// 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")
    
    	// 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")
    
    	if nsavederrors+nerrors == 0 {
    		fninit(xtop)
    	}
    
    	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")
    
    	dumpobj()
    	if asmhdr != "" {
    		dumpasmhdr()
    	}
    
    
    	// Check whether any of the functions we have compiled have gigantic stack frames.
    
    Russ Cox's avatar
    Russ Cox committed
    	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(compilequeue) != 0 {
    		Fatalf("%d uncompiled functions", len(compilequeue))
    	}
    
    
    	logopt.FlushLoggedOpts(Ctxt, myimportpath)
    
    
    	if nerrors+nsavederrors != 0 {
    		errorexit()
    	}
    
    
    	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")
    	}
    
    	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, abi := parts[1], parts[2]
    			if abi != "ABI0" { // Only supported external ABI right now
    				log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abi)
    			}
    
    			// 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] = obj.ABI0
    			} else {
    				symabiRefs[sym] = obj.ABI0
    			}
    		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