Source file src/cmd/compile/internal/gc/main.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gc
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"cmd/compile/internal/base"
    11  	"cmd/compile/internal/deadcode"
    12  	"cmd/compile/internal/devirtualize"
    13  	"cmd/compile/internal/dwarfgen"
    14  	"cmd/compile/internal/escape"
    15  	"cmd/compile/internal/inline"
    16  	"cmd/compile/internal/ir"
    17  	"cmd/compile/internal/logopt"
    18  	"cmd/compile/internal/noder"
    19  	"cmd/compile/internal/pkginit"
    20  	"cmd/compile/internal/reflectdata"
    21  	"cmd/compile/internal/ssa"
    22  	"cmd/compile/internal/ssagen"
    23  	"cmd/compile/internal/typecheck"
    24  	"cmd/compile/internal/types"
    25  	"cmd/internal/dwarf"
    26  	"cmd/internal/obj"
    27  	"cmd/internal/objabi"
    28  	"cmd/internal/src"
    29  	"flag"
    30  	"fmt"
    31  	"internal/buildcfg"
    32  	"log"
    33  	"os"
    34  	"runtime"
    35  	"sort"
    36  )
    37  
    38  // handlePanic ensures that we print out an "internal compiler error" for any panic
    39  // or runtime exception during front-end compiler processing (unless there have
    40  // already been some compiler errors). It may also be invoked from the explicit panic in
    41  // hcrash(), in which case, we pass the panic on through.
    42  func handlePanic() {
    43  	if err := recover(); err != nil {
    44  		if err == "-h" {
    45  			// Force real panic now with -h option (hcrash) - the error
    46  			// information will have already been printed.
    47  			panic(err)
    48  		}
    49  		base.Fatalf("panic: %v", err)
    50  	}
    51  }
    52  
    53  // Main parses flags and Go source files specified in the command-line
    54  // arguments, type-checks the parsed Go package, compiles functions to machine
    55  // code, and finally writes the compiled package definition to disk.
    56  func Main(archInit func(*ssagen.ArchInfo)) {
    57  	base.Timer.Start("fe", "init")
    58  
    59  	defer handlePanic()
    60  
    61  	archInit(&ssagen.Arch)
    62  
    63  	base.Ctxt = obj.Linknew(ssagen.Arch.LinkArch)
    64  	base.Ctxt.DiagFunc = base.Errorf
    65  	base.Ctxt.DiagFlush = base.FlushErrors
    66  	base.Ctxt.Bso = bufio.NewWriter(os.Stdout)
    67  
    68  	// UseBASEntries is preferred because it shaves about 2% off build time, but LLDB, dsymutil, and dwarfdump
    69  	// on Darwin don't support it properly, especially since macOS 10.14 (Mojave).  This is exposed as a flag
    70  	// to allow testing with LLVM tools on Linux, and to help with reporting this bug to the LLVM project.
    71  	// See bugs 31188 and 21945 (CLs 170638, 98075, 72371).
    72  	base.Ctxt.UseBASEntries = base.Ctxt.Headtype != objabi.Hdarwin
    73  
    74  	types.LocalPkg = types.NewPkg("", "")
    75  	types.LocalPkg.Prefix = "\"\""
    76  
    77  	// We won't know localpkg's height until after import
    78  	// processing. In the mean time, set to MaxPkgHeight to ensure
    79  	// height comparisons at least work until then.
    80  	types.LocalPkg.Height = types.MaxPkgHeight
    81  
    82  	// pseudo-package, for scoping
    83  	types.BuiltinPkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin?
    84  	types.BuiltinPkg.Prefix = "go.builtin"            // not go%2ebuiltin
    85  
    86  	// pseudo-package, accessed by import "unsafe"
    87  	types.UnsafePkg = types.NewPkg("unsafe", "unsafe")
    88  
    89  	// Pseudo-package that contains the compiler's builtin
    90  	// declarations for package runtime. These are declared in a
    91  	// separate package to avoid conflicts with package runtime's
    92  	// actual declarations, which may differ intentionally but
    93  	// insignificantly.
    94  	ir.Pkgs.Runtime = types.NewPkg("go.runtime", "runtime")
    95  	ir.Pkgs.Runtime.Prefix = "runtime"
    96  
    97  	// pseudo-packages used in symbol tables
    98  	ir.Pkgs.Itab = types.NewPkg("go.itab", "go.itab")
    99  	ir.Pkgs.Itab.Prefix = "go.itab" // not go%2eitab
   100  
   101  	// pseudo-package used for methods with anonymous receivers
   102  	ir.Pkgs.Go = types.NewPkg("go", "")
   103  
   104  	base.DebugSSA = ssa.PhaseOption
   105  	base.ParseFlags()
   106  
   107  	// Record flags that affect the build result. (And don't
   108  	// record flags that don't, since that would cause spurious
   109  	// changes in the binary.)
   110  	dwarfgen.RecordFlags("B", "N", "l", "msan", "race", "asan", "shared", "dynlink", "dwarf", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
   111  
   112  	if !base.EnableTrace && base.Flag.LowerT {
   113  		log.Fatalf("compiler not built with support for -t")
   114  	}
   115  
   116  	// Enable inlining (after RecordFlags, to avoid recording the rewritten -l).  For now:
   117  	//	default: inlining on.  (Flag.LowerL == 1)
   118  	//	-l: inlining off  (Flag.LowerL == 0)
   119  	//	-l=2, -l=3: inlining on again, with extra debugging (Flag.LowerL > 1)
   120  	if base.Flag.LowerL <= 1 {
   121  		base.Flag.LowerL = 1 - base.Flag.LowerL
   122  	}
   123  
   124  	if base.Flag.SmallFrames {
   125  		ir.MaxStackVarSize = 128 * 1024
   126  		ir.MaxImplicitStackVarSize = 16 * 1024
   127  	}
   128  
   129  	if base.Flag.Dwarf {
   130  		base.Ctxt.DebugInfo = dwarfgen.Info
   131  		base.Ctxt.GenAbstractFunc = dwarfgen.AbstractFunc
   132  		base.Ctxt.DwFixups = obj.NewDwarfFixupTable(base.Ctxt)
   133  	} else {
   134  		// turn off inline generation if no dwarf at all
   135  		base.Flag.GenDwarfInl = 0
   136  		base.Ctxt.Flag_locationlists = false
   137  	}
   138  	if base.Ctxt.Flag_locationlists && len(base.Ctxt.Arch.DWARFRegisters) == 0 {
   139  		log.Fatalf("location lists requested but register mapping not available on %v", base.Ctxt.Arch.Name)
   140  	}
   141  
   142  	types.ParseLangFlag()
   143  
   144  	symABIs := ssagen.NewSymABIs(base.Ctxt.Pkgpath)
   145  	if base.Flag.SymABIs != "" {
   146  		symABIs.ReadSymABIs(base.Flag.SymABIs)
   147  	}
   148  
   149  	if base.Compiling(base.NoInstrumentPkgs) {
   150  		base.Flag.Race = false
   151  		base.Flag.MSan = false
   152  		base.Flag.ASan = false
   153  	}
   154  
   155  	ssagen.Arch.LinkArch.Init(base.Ctxt)
   156  	startProfile()
   157  	if base.Flag.Race || base.Flag.MSan || base.Flag.ASan {
   158  		base.Flag.Cfg.Instrumenting = true
   159  	}
   160  	if base.Flag.Dwarf {
   161  		dwarf.EnableLogging(base.Debug.DwarfInl != 0)
   162  	}
   163  	if base.Debug.SoftFloat != 0 {
   164  		ssagen.Arch.SoftFloat = true
   165  	}
   166  
   167  	if base.Flag.JSON != "" { // parse version,destination from json logging optimization.
   168  		logopt.LogJsonOption(base.Flag.JSON)
   169  	}
   170  
   171  	ir.EscFmt = escape.Fmt
   172  	ir.IsIntrinsicCall = ssagen.IsIntrinsicCall
   173  	inline.SSADumpInline = ssagen.DumpInline
   174  	ssagen.InitEnv()
   175  	ssagen.InitTables()
   176  
   177  	types.PtrSize = ssagen.Arch.LinkArch.PtrSize
   178  	types.RegSize = ssagen.Arch.LinkArch.RegSize
   179  	types.MaxWidth = ssagen.Arch.MAXWIDTH
   180  
   181  	typecheck.Target = new(ir.Package)
   182  
   183  	typecheck.NeedRuntimeType = reflectdata.NeedRuntimeType // TODO(rsc): TypeSym for lock?
   184  
   185  	base.AutogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
   186  
   187  	typecheck.InitUniverse()
   188  	typecheck.InitRuntime()
   189  
   190  	// Parse and typecheck input.
   191  	noder.LoadPackage(flag.Args())
   192  
   193  	dwarfgen.RecordPackageName()
   194  
   195  	// Prepare for backend processing. This must happen before pkginit,
   196  	// because it generates itabs for initializing global variables.
   197  	ssagen.InitConfig()
   198  
   199  	// Create "init" function for package-scope variable initialization
   200  	// statements, if any.
   201  	//
   202  	// Note: This needs to happen early, before any optimizations. The
   203  	// Go spec defines a precise order than initialization should be
   204  	// carried out in, and even mundane optimizations like dead code
   205  	// removal can skew the results (e.g., #43444).
   206  	pkginit.MakeInit()
   207  
   208  	// Stability quirk: sort top-level declarations, so we're not
   209  	// sensitive to the order that functions are added. In particular,
   210  	// the order that noder+typecheck add function closures is very
   211  	// subtle, and not important to reproduce.
   212  	if base.Debug.UnifiedQuirks != 0 {
   213  		s := typecheck.Target.Decls
   214  		sort.SliceStable(s, func(i, j int) bool {
   215  			return s[i].Pos().Before(s[j].Pos())
   216  		})
   217  	}
   218  
   219  	// Eliminate some obviously dead code.
   220  	// Must happen after typechecking.
   221  	for _, n := range typecheck.Target.Decls {
   222  		if n.Op() == ir.ODCLFUNC {
   223  			deadcode.Func(n.(*ir.Func))
   224  		}
   225  	}
   226  
   227  	// Compute Addrtaken for names.
   228  	// We need to wait until typechecking is done so that when we see &x[i]
   229  	// we know that x has its address taken if x is an array, but not if x is a slice.
   230  	// We compute Addrtaken in bulk here.
   231  	// After this phase, we maintain Addrtaken incrementally.
   232  	if typecheck.DirtyAddrtaken {
   233  		typecheck.ComputeAddrtaken(typecheck.Target.Decls)
   234  		typecheck.DirtyAddrtaken = false
   235  	}
   236  	typecheck.IncrementalAddrtaken = true
   237  
   238  	if base.Debug.TypecheckInl != 0 {
   239  		// Typecheck imported function bodies if Debug.l > 1,
   240  		// otherwise lazily when used or re-exported.
   241  		typecheck.AllImportedBodies()
   242  	}
   243  
   244  	// Inlining
   245  	base.Timer.Start("fe", "inlining")
   246  	if base.Flag.LowerL != 0 {
   247  		inline.InlinePackage()
   248  	}
   249  	noder.MakeWrappers(typecheck.Target) // must happen after inlining
   250  
   251  	// Devirtualize.
   252  	for _, n := range typecheck.Target.Decls {
   253  		if n.Op() == ir.ODCLFUNC {
   254  			devirtualize.Func(n.(*ir.Func))
   255  		}
   256  	}
   257  	ir.CurFunc = nil
   258  
   259  	// Build init task, if needed.
   260  	if initTask := pkginit.Task(); initTask != nil {
   261  		typecheck.Export(initTask)
   262  	}
   263  
   264  	// Generate ABI wrappers. Must happen before escape analysis
   265  	// and doesn't benefit from dead-coding or inlining.
   266  	symABIs.GenABIWrappers()
   267  
   268  	// Escape analysis.
   269  	// Required for moving heap allocations onto stack,
   270  	// which in turn is required by the closure implementation,
   271  	// which stores the addresses of stack variables into the closure.
   272  	// If the closure does not escape, it needs to be on the stack
   273  	// or else the stack copier will not update it.
   274  	// Large values are also moved off stack in escape analysis;
   275  	// because large values may contain pointers, it must happen early.
   276  	base.Timer.Start("fe", "escapes")
   277  	escape.Funcs(typecheck.Target.Decls)
   278  
   279  	// TODO(mdempsky): This is a hack. We need a proper, global work
   280  	// queue for scheduling function compilation so components don't
   281  	// need to adjust their behavior depending on when they're called.
   282  	reflectdata.AfterGlobalEscapeAnalysis = true
   283  
   284  	// Collect information for go:nowritebarrierrec
   285  	// checking. This must happen before transforming closures during Walk
   286  	// We'll do the final check after write barriers are
   287  	// inserted.
   288  	if base.Flag.CompilingRuntime {
   289  		ssagen.EnableNoWriteBarrierRecCheck()
   290  	}
   291  
   292  	ir.CurFunc = nil
   293  
   294  	// Compile top level functions.
   295  	// Don't use range--walk can add functions to Target.Decls.
   296  	base.Timer.Start("be", "compilefuncs")
   297  	fcount := int64(0)
   298  	for i := 0; i < len(typecheck.Target.Decls); i++ {
   299  		if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
   300  			// Don't try compiling dead hidden closure.
   301  			if fn.IsDeadcodeClosure() {
   302  				continue
   303  			}
   304  			enqueueFunc(fn)
   305  			fcount++
   306  		}
   307  	}
   308  	base.Timer.AddEvent(fcount, "funcs")
   309  
   310  	compileFunctions()
   311  
   312  	if base.Flag.CompilingRuntime {
   313  		// Write barriers are now known. Check the call graph.
   314  		ssagen.NoWriteBarrierRecCheck()
   315  	}
   316  
   317  	// Finalize DWARF inline routine DIEs, then explicitly turn off
   318  	// DWARF inlining gen so as to avoid problems with generated
   319  	// method wrappers.
   320  	if base.Ctxt.DwFixups != nil {
   321  		base.Ctxt.DwFixups.Finalize(base.Ctxt.Pkgpath, base.Debug.DwarfInl != 0)
   322  		base.Ctxt.DwFixups = nil
   323  		base.Flag.GenDwarfInl = 0
   324  	}
   325  
   326  	// Write object data to disk.
   327  	base.Timer.Start("be", "dumpobj")
   328  	dumpdata()
   329  	base.Ctxt.NumberSyms()
   330  	dumpobj()
   331  	if base.Flag.AsmHdr != "" {
   332  		dumpasmhdr()
   333  	}
   334  
   335  	ssagen.CheckLargeStacks()
   336  	typecheck.CheckFuncStack()
   337  
   338  	if len(compilequeue) != 0 {
   339  		base.Fatalf("%d uncompiled functions", len(compilequeue))
   340  	}
   341  
   342  	logopt.FlushLoggedOpts(base.Ctxt, base.Ctxt.Pkgpath)
   343  	base.ExitIfErrors()
   344  
   345  	base.FlushErrors()
   346  	base.Timer.Stop()
   347  
   348  	if base.Flag.Bench != "" {
   349  		if err := writebench(base.Flag.Bench); err != nil {
   350  			log.Fatalf("cannot write benchmark data: %v", err)
   351  		}
   352  	}
   353  }
   354  
   355  func writebench(filename string) error {
   356  	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
   357  	if err != nil {
   358  		return err
   359  	}
   360  
   361  	var buf bytes.Buffer
   362  	fmt.Fprintln(&buf, "commit:", buildcfg.Version)
   363  	fmt.Fprintln(&buf, "goos:", runtime.GOOS)
   364  	fmt.Fprintln(&buf, "goarch:", runtime.GOARCH)
   365  	base.Timer.Write(&buf, "BenchmarkCompile:"+base.Ctxt.Pkgpath+":")
   366  
   367  	n, err := f.Write(buf.Bytes())
   368  	if err != nil {
   369  		return err
   370  	}
   371  	if n != buf.Len() {
   372  		panic("bad writer")
   373  	}
   374  
   375  	return f.Close()
   376  }
   377  
   378  func makePos(b *src.PosBase, line, col uint) src.XPos {
   379  	return base.Ctxt.PosTable.XPos(src.MakePos(b, line, col))
   380  }
   381  

View as plain text