summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/gc/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/gc/main.go')
-rw-r--r--src/cmd/compile/internal/gc/main.go380
1 files changed, 380 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..4c4a724
--- /dev/null
+++ b/src/cmd/compile/internal/gc/main.go
@@ -0,0 +1,380 @@
+// 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/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/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"
+ "sort"
+)
+
+// 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
+
+ types.LocalPkg = types.NewPkg("", "")
+ types.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.
+ types.LocalPkg.Height = types.MaxPkgHeight
+
+ // pseudo-package, for scoping
+ types.BuiltinPkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin?
+ types.BuiltinPkg.Prefix = "go.builtin" // not go%2ebuiltin
+
+ // 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" // not go%2eitab
+
+ // pseudo-package used for methods with anonymous receivers
+ ir.Pkgs.Go = types.NewPkg("go", "")
+
+ base.DebugSSA = ssa.PhaseOption
+ base.ParseFlags()
+
+ // 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(base.Ctxt.Pkgpath)
+ 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())
+
+ dwarfgen.RecordPackageName()
+
+ // Prepare for backend processing. This must happen before pkginit,
+ // because it generates itabs for initializing global variables.
+ ssagen.InitConfig()
+
+ // 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()
+
+ // Stability quirk: sort top-level declarations, so we're not
+ // sensitive to the order that functions are added. In particular,
+ // the order that noder+typecheck add function closures is very
+ // subtle, and not important to reproduce.
+ if base.Debug.UnifiedQuirks != 0 {
+ s := typecheck.Target.Decls
+ sort.SliceStable(s, func(i, j int) bool {
+ return s[i].Pos().Before(s[j].Pos())
+ })
+ }
+
+ // 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()
+ }
+
+ // Inlining
+ base.Timer.Start("fe", "inlining")
+ if base.Flag.LowerL != 0 {
+ inline.InlinePackage()
+ }
+ 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))
+}