// 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 gc import ( "bufio" "bytes" "cmd/compile/internal/base" "cmd/compile/internal/coverage" "cmd/compile/internal/dwarfgen" "cmd/compile/internal/escape" "cmd/compile/internal/inline" "cmd/compile/internal/inline/interleaved" "cmd/compile/internal/ir" "cmd/compile/internal/logopt" "cmd/compile/internal/loopvar" "cmd/compile/internal/noder" "cmd/compile/internal/pgo" "cmd/compile/internal/pkginit" "cmd/compile/internal/reflectdata" "cmd/compile/internal/rttype" "cmd/compile/internal/ssa" "cmd/compile/internal/ssagen" "cmd/compile/internal/staticinit" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/dwarf" "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/src" "flag" "fmt" "internal/buildcfg" "log" "os" "runtime" ) // handlePanic ensures that we print out an "internal compiler error" for any panic // or runtime exception during front-end compiler processing (unless there have // already been some compiler errors). It may also be invoked from the explicit panic in // hcrash(), in which case, we pass the panic on through. func handlePanic() { if err := recover(); err != nil { if err == "-h" { // Force real panic now with -h option (hcrash) - the error // information will have already been printed. panic(err) } base.Fatalf("panic: %v", err) } } // 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(*ssagen.ArchInfo)) { base.Timer.Start("fe", "init") defer handlePanic() archInit(&ssagen.Arch) base.Ctxt = obj.Linknew(ssagen.Arch.LinkArch) base.Ctxt.DiagFunc = base.Errorf base.Ctxt.DiagFlush = base.FlushErrors base.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). base.Ctxt.UseBASEntries = base.Ctxt.Headtype != objabi.Hdarwin base.DebugSSA = ssa.PhaseOption base.ParseFlags() if os.Getenv("GOGC") == "" { // GOGC set disables starting heap adjustment // More processors will use more heap, but assume that more memory is available. // So 1 processor -> 40MB, 4 -> 64MB, 12 -> 128MB base.AdjustStartingHeap(uint64(32+8*base.Flag.LowerC) << 20) } types.LocalPkg = types.NewPkg(base.Ctxt.Pkgpath, "") // pseudo-package, for scoping types.BuiltinPkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin? types.BuiltinPkg.Prefix = "go:builtin" // pseudo-package, accessed by import "unsafe" types.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. ir.Pkgs.Runtime = types.NewPkg("go.runtime", "runtime") ir.Pkgs.Runtime.Prefix = "runtime" // pseudo-packages used in symbol tables ir.Pkgs.Itab = types.NewPkg("go.itab", "go.itab") ir.Pkgs.Itab.Prefix = "go:itab" // pseudo-package used for methods with anonymous receivers ir.Pkgs.Go = types.NewPkg("go", "") // pseudo-package for use with code coverage instrumentation. ir.Pkgs.Coverage = types.NewPkg("go.coverage", "runtime/coverage") ir.Pkgs.Coverage.Prefix = "runtime/coverage" // Record flags that affect the build result. (And don't // record flags that don't, since that would cause spurious // changes in the binary.) dwarfgen.RecordFlags("B", "N", "l", "msan", "race", "asan", "shared", "dynlink", "dwarf", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre") if !base.EnableTrace && base.Flag.LowerT { log.Fatalf("compiler not built with support for -t") } // Enable inlining (after RecordFlags, to avoid recording the rewritten -l). For now: // default: inlining on. (Flag.LowerL == 1) // -l: inlining off (Flag.LowerL == 0) // -l=2, -l=3: inlining on again, with extra debugging (Flag.LowerL > 1) if base.Flag.LowerL <= 1 { base.Flag.LowerL = 1 - base.Flag.LowerL } if base.Flag.SmallFrames { ir.MaxStackVarSize = 128 * 1024 ir.MaxImplicitStackVarSize = 16 * 1024 } if base.Flag.Dwarf { base.Ctxt.DebugInfo = dwarfgen.Info base.Ctxt.GenAbstractFunc = dwarfgen.AbstractFunc base.Ctxt.DwFixups = obj.NewDwarfFixupTable(base.Ctxt) } else { // turn off inline generation if no dwarf at all base.Flag.GenDwarfInl = 0 base.Ctxt.Flag_locationlists = false } if base.Ctxt.Flag_locationlists && len(base.Ctxt.Arch.DWARFRegisters) == 0 { log.Fatalf("location lists requested but register mapping not available on %v", base.Ctxt.Arch.Name) } types.ParseLangFlag() symABIs := ssagen.NewSymABIs() if base.Flag.SymABIs != "" { symABIs.ReadSymABIs(base.Flag.SymABIs) } if objabi.LookupPkgSpecial(base.Ctxt.Pkgpath).NoInstrument { base.Flag.Race = false base.Flag.MSan = false base.Flag.ASan = false } ssagen.Arch.LinkArch.Init(base.Ctxt) startProfile() if base.Flag.Race || base.Flag.MSan || base.Flag.ASan { base.Flag.Cfg.Instrumenting = true } if base.Flag.Dwarf { dwarf.EnableLogging(base.Debug.DwarfInl != 0) } if base.Debug.SoftFloat != 0 { ssagen.Arch.SoftFloat = true } if base.Flag.JSON != "" { // parse version,destination from json logging optimization. logopt.LogJsonOption(base.Flag.JSON) } ir.EscFmt = escape.Fmt ir.IsIntrinsicCall = ssagen.IsIntrinsicCall inline.SSADumpInline = ssagen.DumpInline ssagen.InitEnv() ssagen.InitTables() types.PtrSize = ssagen.Arch.LinkArch.PtrSize types.RegSize = ssagen.Arch.LinkArch.RegSize types.MaxWidth = ssagen.Arch.MAXWIDTH typecheck.Target = new(ir.Package) base.AutogeneratedPos = makePos(src.NewFileBase("", ""), 1, 0) typecheck.InitUniverse() typecheck.InitRuntime() rttype.Init() // Parse and typecheck input. noder.LoadPackage(flag.Args()) // As a convenience to users (toolchain maintainers, in particular), // when compiling a package named "main", we default the package // path to "main" if the -p flag was not specified. if base.Ctxt.Pkgpath == obj.UnlinkablePkg && types.LocalPkg.Name == "main" { base.Ctxt.Pkgpath = "main" types.LocalPkg.Path = "main" types.LocalPkg.Prefix = "main" } dwarfgen.RecordPackageName() // Prepare for backend processing. ssagen.InitConfig() // Apply coverage fixups, if applicable. coverage.Fixup() // Read profile file and build profile-graph and weighted-call-graph. base.Timer.Start("fe", "pgo-load-profile") var profile *pgo.Profile if base.Flag.PgoProfile != "" { var err error profile, err = pgo.New(base.Flag.PgoProfile) if err != nil { log.Fatalf("%s: PGO error: %v", base.Flag.PgoProfile, err) } } // Interleaved devirtualization and inlining. base.Timer.Start("fe", "devirtualize-and-inline") interleaved.DevirtualizeAndInlinePackage(typecheck.Target, profile) noder.MakeWrappers(typecheck.Target) // must happen after inlining // Get variable capture right in for loops. var transformed []loopvar.VarAndLoop for _, fn := range typecheck.Target.Funcs { transformed = append(transformed, loopvar.ForCapture(fn)...) } ir.CurFunc = nil // Build init task, if needed. pkginit.MakeTask() // Generate ABI wrappers. Must happen before escape analysis // and doesn't benefit from dead-coding or inlining. symABIs.GenABIWrappers() // 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. base.Timer.Start("fe", "escapes") escape.Funcs(typecheck.Target.Funcs) loopvar.LogTransformations(transformed) // Collect information for go:nowritebarrierrec // checking. This must happen before transforming closures during Walk // We'll do the final check after write barriers are // inserted. if base.Flag.CompilingRuntime { ssagen.EnableNoWriteBarrierRecCheck() } ir.CurFunc = nil reflectdata.WriteBasicTypes() // Compile top-level declarations. // // There are cyclic dependencies between all of these phases, so we // need to iterate all of them until we reach a fixed point. base.Timer.Start("be", "compilefuncs") for nextFunc, nextExtern := 0, 0; ; { reflectdata.WriteRuntimeTypes() if nextExtern < len(typecheck.Target.Externs) { switch n := typecheck.Target.Externs[nextExtern]; n.Op() { case ir.ONAME: dumpGlobal(n) case ir.OLITERAL: dumpGlobalConst(n) case ir.OTYPE: reflectdata.NeedRuntimeType(n.Type()) } nextExtern++ continue } if nextFunc < len(typecheck.Target.Funcs) { enqueueFunc(typecheck.Target.Funcs[nextFunc]) nextFunc++ continue } // The SSA backend supports using multiple goroutines, so keep it // as late as possible to maximize how much work we can batch and // process concurrently. if len(compilequeue) != 0 { compileFunctions() continue } // Finalize DWARF inline routine DIEs, then explicitly turn off // further DWARF inlining generation to avoid problems with // generated method wrappers. // // Note: The DWARF fixup code for inlined calls currently doesn't // allow multiple invocations, so we intentionally run it just // once after everything else. Worst case, some generated // functions have slightly larger DWARF DIEs. if base.Ctxt.DwFixups != nil { base.Ctxt.DwFixups.Finalize(base.Ctxt.Pkgpath, base.Debug.DwarfInl != 0) base.Ctxt.DwFixups = nil base.Flag.GenDwarfInl = 0 continue // may have called reflectdata.TypeLinksym (#62156) } break } base.Timer.AddEvent(int64(len(typecheck.Target.Funcs)), "funcs") if base.Flag.CompilingRuntime { // Write barriers are now known. Check the call graph. ssagen.NoWriteBarrierRecCheck() } // Add keep relocations for global maps. if base.Debug.WrapGlobalMapCtl != 1 { staticinit.AddKeepRelocations() } // Write object data to disk. base.Timer.Start("be", "dumpobj") dumpdata() base.Ctxt.NumberSyms() dumpobj() if base.Flag.AsmHdr != "" { dumpasmhdr() } ssagen.CheckLargeStacks() typecheck.CheckFuncStack() if len(compilequeue) != 0 { base.Fatalf("%d uncompiled functions", len(compilequeue)) } logopt.FlushLoggedOpts(base.Ctxt, base.Ctxt.Pkgpath) base.ExitIfErrors() base.FlushErrors() base.Timer.Stop() if base.Flag.Bench != "" { if err := writebench(base.Flag.Bench); err != nil { log.Fatalf("cannot write benchmark data: %v", err) } } } 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:", buildcfg.Version) fmt.Fprintln(&buf, "goos:", runtime.GOOS) fmt.Fprintln(&buf, "goarch:", runtime.GOARCH) base.Timer.Write(&buf, "BenchmarkCompile:"+base.Ctxt.Pkgpath+":") n, err := f.Write(buf.Bytes()) if err != nil { return err } if n != buf.Len() { panic("bad writer") } return f.Close() } func makePos(b *src.PosBase, line, col uint) src.XPos { return base.Ctxt.PosTable.XPos(src.MakePos(b, line, col)) }