diff options
Diffstat (limited to 'src/cmd/compile/internal/gc/main.go')
-rw-r--r-- | src/cmd/compile/internal/gc/main.go | 402 |
1 files changed, 402 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..4a37548 --- /dev/null +++ b/src/cmd/compile/internal/gc/main.go @@ -0,0 +1,402 @@ +// 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/deadcode" + "cmd/compile/internal/devirtualize" + "cmd/compile/internal/dwarfgen" + "cmd/compile/internal/escape" + "cmd/compile/internal/inline" + "cmd/compile/internal/ir" + "cmd/compile/internal/logopt" + "cmd/compile/internal/noder" + "cmd/compile/internal/pgo" + "cmd/compile/internal/pkginit" + "cmd/compile/internal/reflectdata" + "cmd/compile/internal/ssa" + "cmd/compile/internal/ssagen" + "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 base.Compiling(base.NoInstrumentPkgs) { + 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) + + typecheck.NeedRuntimeType = reflectdata.NeedRuntimeType // TODO(rsc): TypeSym for lock? + + base.AutogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0) + + typecheck.InitUniverse() + typecheck.InitRuntime() + + // 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. This must happen before pkginit, + // because it generates itabs for initializing global variables. + ssagen.InitConfig() + + // First part of coverage fixup (if applicable). + var cnames coverage.Names + if base.Flag.Cfg.CoverageInfo != nil { + cnames = coverage.FixupVars() + } + + // Create "init" function for package-scope variable initialization + // statements, if any. + // + // Note: This needs to happen early, before any optimizations. The + // Go spec defines a precise order than initialization should be + // carried out in, and even mundane optimizations like dead code + // removal can skew the results (e.g., #43444). + pkginit.MakeInit() + + // Second part of code coverage fixup (init func modification), + // if applicable. + if base.Flag.Cfg.CoverageInfo != nil { + coverage.FixupInit(cnames) + } + + // Eliminate some obviously dead code. + // Must happen after typechecking. + for _, n := range typecheck.Target.Decls { + if n.Op() == ir.ODCLFUNC { + deadcode.Func(n.(*ir.Func)) + } + } + + // Compute Addrtaken for names. + // We need to wait until typechecking is done so that when we see &x[i] + // we know that x has its address taken if x is an array, but not if x is a slice. + // We compute Addrtaken in bulk here. + // After this phase, we maintain Addrtaken incrementally. + if typecheck.DirtyAddrtaken { + typecheck.ComputeAddrtaken(typecheck.Target.Decls) + typecheck.DirtyAddrtaken = false + } + typecheck.IncrementalAddrtaken = true + + if base.Debug.TypecheckInl != 0 { + // Typecheck imported function bodies if Debug.l > 1, + // otherwise lazily when used or re-exported. + typecheck.AllImportedBodies() + } + + // Read profile file and build profile-graph and weighted-call-graph. + base.Timer.Start("fe", "pgoprofile") + var profile *pgo.Profile + if base.Flag.PgoProfile != "" { + profile = pgo.New(base.Flag.PgoProfile) + } + + // Inlining + base.Timer.Start("fe", "inlining") + if base.Flag.LowerL != 0 { + inline.InlinePackage(profile) + } + noder.MakeWrappers(typecheck.Target) // must happen after inlining + + // Devirtualize. + for _, n := range typecheck.Target.Decls { + if n.Op() == ir.ODCLFUNC { + devirtualize.Func(n.(*ir.Func)) + } + } + ir.CurFunc = nil + + // Build init task, if needed. + if initTask := pkginit.Task(); initTask != nil { + typecheck.Export(initTask) + } + + // 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.Decls) + + // TODO(mdempsky): This is a hack. We need a proper, global work + // queue for scheduling function compilation so components don't + // need to adjust their behavior depending on when they're called. + reflectdata.AfterGlobalEscapeAnalysis = true + + // 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 + + // Compile top level functions. + // Don't use range--walk can add functions to Target.Decls. + base.Timer.Start("be", "compilefuncs") + fcount := int64(0) + for i := 0; i < len(typecheck.Target.Decls); i++ { + if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok { + // Don't try compiling dead hidden closure. + if fn.IsDeadcodeClosure() { + continue + } + enqueueFunc(fn) + fcount++ + } + } + base.Timer.AddEvent(fcount, "funcs") + + compileFunctions() + + if base.Flag.CompilingRuntime { + // Write barriers are now known. Check the call graph. + ssagen.NoWriteBarrierRecCheck() + } + + // Finalize DWARF inline routine DIEs, then explicitly turn off + // DWARF inlining gen so as to avoid problems with generated + // method wrappers. + if base.Ctxt.DwFixups != nil { + base.Ctxt.DwFixups.Finalize(base.Ctxt.Pkgpath, base.Debug.DwarfInl != 0) + base.Ctxt.DwFixups = nil + base.Flag.GenDwarfInl = 0 + } + + // 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)) +} |