github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/gc/main.go (about)

     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  	"flag"
    11  	"fmt"
    12  	"log"
    13  	"os"
    14  	"runtime"
    15  
    16  	"github.com/go-asm/go/buildcfg"
    17  	"github.com/go-asm/go/cmd/compile/base"
    18  	"github.com/go-asm/go/cmd/compile/coverage"
    19  	"github.com/go-asm/go/cmd/compile/dwarfgen"
    20  	"github.com/go-asm/go/cmd/compile/escape"
    21  	"github.com/go-asm/go/cmd/compile/inline"
    22  	"github.com/go-asm/go/cmd/compile/inline/interleaved"
    23  	"github.com/go-asm/go/cmd/compile/ir"
    24  	"github.com/go-asm/go/cmd/compile/logopt"
    25  	"github.com/go-asm/go/cmd/compile/loopvar"
    26  	"github.com/go-asm/go/cmd/compile/noder"
    27  	"github.com/go-asm/go/cmd/compile/pgo"
    28  	"github.com/go-asm/go/cmd/compile/pkginit"
    29  	"github.com/go-asm/go/cmd/compile/reflectdata"
    30  	"github.com/go-asm/go/cmd/compile/rttype"
    31  	"github.com/go-asm/go/cmd/compile/ssa"
    32  	"github.com/go-asm/go/cmd/compile/ssagen"
    33  	"github.com/go-asm/go/cmd/compile/staticinit"
    34  	"github.com/go-asm/go/cmd/compile/typecheck"
    35  	"github.com/go-asm/go/cmd/compile/types"
    36  	"github.com/go-asm/go/cmd/dwarf"
    37  	"github.com/go-asm/go/cmd/obj"
    38  	"github.com/go-asm/go/cmd/objabi"
    39  	"github.com/go-asm/go/cmd/src"
    40  )
    41  
    42  // handlePanic ensures that we print out an "internal compiler error" for any panic
    43  // or runtime exception during front-end compiler processing (unless there have
    44  // already been some compiler errors). It may also be invoked from the explicit panic in
    45  // hcrash(), in which case, we pass the panic on through.
    46  func handlePanic() {
    47  	if err := recover(); err != nil {
    48  		if err == "-h" {
    49  			// Force real panic now with -h option (hcrash) - the error
    50  			// information will have already been printed.
    51  			panic(err)
    52  		}
    53  		base.Fatalf("panic: %v", err)
    54  	}
    55  }
    56  
    57  // Main parses flags and Go source files specified in the command-line
    58  // arguments, type-checks the parsed Go package, compiles functions to machine
    59  // code, and finally writes the compiled package definition to disk.
    60  func Main(archInit func(*ssagen.ArchInfo)) {
    61  	base.Timer.Start("fe", "init")
    62  
    63  	defer handlePanic()
    64  
    65  	archInit(&ssagen.Arch)
    66  
    67  	base.Ctxt = obj.Linknew(ssagen.Arch.LinkArch)
    68  	base.Ctxt.DiagFunc = base.Errorf
    69  	base.Ctxt.DiagFlush = base.FlushErrors
    70  	base.Ctxt.Bso = bufio.NewWriter(os.Stdout)
    71  
    72  	// UseBASEntries is preferred because it shaves about 2% off build time, but LLDB, dsymutil, and dwarfdump
    73  	// on Darwin don't support it properly, especially since macOS 10.14 (Mojave).  This is exposed as a flag
    74  	// to allow testing with LLVM tools on Linux, and to help with reporting this bug to the LLVM project.
    75  	// See bugs 31188 and 21945 (CLs 170638, 98075, 72371).
    76  	base.Ctxt.UseBASEntries = base.Ctxt.Headtype != objabi.Hdarwin
    77  
    78  	base.DebugSSA = ssa.PhaseOption
    79  	base.ParseFlags()
    80  
    81  	if os.Getenv("GOGC") == "" { // GOGC set disables starting heap adjustment
    82  		// More processors will use more heap, but assume that more memory is available.
    83  		// So 1 processor -> 40MB, 4 -> 64MB, 12 -> 128MB
    84  		base.AdjustStartingHeap(uint64(32+8*base.Flag.LowerC) << 20)
    85  	}
    86  
    87  	types.LocalPkg = types.NewPkg(base.Ctxt.Pkgpath, "")
    88  
    89  	// pseudo-package, for scoping
    90  	types.BuiltinPkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin?
    91  	types.BuiltinPkg.Prefix = "go:builtin"
    92  
    93  	// pseudo-package, accessed by import "unsafe"
    94  	types.UnsafePkg = types.NewPkg("unsafe", "unsafe")
    95  
    96  	// Pseudo-package that contains the compiler's builtin
    97  	// declarations for package runtime. These are declared in a
    98  	// separate package to avoid conflicts with package runtime's
    99  	// actual declarations, which may differ intentionally but
   100  	// insignificantly.
   101  	ir.Pkgs.Runtime = types.NewPkg("go.runtime", "runtime")
   102  	ir.Pkgs.Runtime.Prefix = "runtime"
   103  
   104  	// pseudo-packages used in symbol tables
   105  	ir.Pkgs.Itab = types.NewPkg("go.itab", "go.itab")
   106  	ir.Pkgs.Itab.Prefix = "go:itab"
   107  
   108  	// pseudo-package used for methods with anonymous receivers
   109  	ir.Pkgs.Go = types.NewPkg("go", "")
   110  
   111  	// pseudo-package for use with code coverage instrumentation.
   112  	ir.Pkgs.Coverage = types.NewPkg("go.coverage", "runtime/coverage")
   113  	ir.Pkgs.Coverage.Prefix = "runtime/coverage"
   114  
   115  	// Record flags that affect the build result. (And don't
   116  	// record flags that don't, since that would cause spurious
   117  	// changes in the binary.)
   118  	dwarfgen.RecordFlags("B", "N", "l", "msan", "race", "asan", "shared", "dynlink", "dwarf", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
   119  
   120  	if !base.EnableTrace && base.Flag.LowerT {
   121  		log.Fatalf("compiler not built with support for -t")
   122  	}
   123  
   124  	// Enable inlining (after RecordFlags, to avoid recording the rewritten -l).  For now:
   125  	//	default: inlining on.  (Flag.LowerL == 1)
   126  	//	-l: inlining off  (Flag.LowerL == 0)
   127  	//	-l=2, -l=3: inlining on again, with extra debugging (Flag.LowerL > 1)
   128  	if base.Flag.LowerL <= 1 {
   129  		base.Flag.LowerL = 1 - base.Flag.LowerL
   130  	}
   131  
   132  	if base.Flag.SmallFrames {
   133  		ir.MaxStackVarSize = 128 * 1024
   134  		ir.MaxImplicitStackVarSize = 16 * 1024
   135  	}
   136  
   137  	if base.Flag.Dwarf {
   138  		base.Ctxt.DebugInfo = dwarfgen.Info
   139  		base.Ctxt.GenAbstractFunc = dwarfgen.AbstractFunc
   140  		base.Ctxt.DwFixups = obj.NewDwarfFixupTable(base.Ctxt)
   141  	} else {
   142  		// turn off inline generation if no dwarf at all
   143  		base.Flag.GenDwarfInl = 0
   144  		base.Ctxt.Flag_locationlists = false
   145  	}
   146  	if base.Ctxt.Flag_locationlists && len(base.Ctxt.Arch.DWARFRegisters) == 0 {
   147  		log.Fatalf("location lists requested but register mapping not available on %v", base.Ctxt.Arch.Name)
   148  	}
   149  
   150  	types.ParseLangFlag()
   151  
   152  	symABIs := ssagen.NewSymABIs()
   153  	if base.Flag.SymABIs != "" {
   154  		symABIs.ReadSymABIs(base.Flag.SymABIs)
   155  	}
   156  
   157  	if objabi.LookupPkgSpecial(base.Ctxt.Pkgpath).NoInstrument {
   158  		base.Flag.Race = false
   159  		base.Flag.MSan = false
   160  		base.Flag.ASan = false
   161  	}
   162  
   163  	ssagen.Arch.LinkArch.Init(base.Ctxt)
   164  	startProfile()
   165  	if base.Flag.Race || base.Flag.MSan || base.Flag.ASan {
   166  		base.Flag.Cfg.Instrumenting = true
   167  	}
   168  	if base.Flag.Dwarf {
   169  		dwarf.EnableLogging(base.Debug.DwarfInl != 0)
   170  	}
   171  	if base.Debug.SoftFloat != 0 {
   172  		ssagen.Arch.SoftFloat = true
   173  	}
   174  
   175  	if base.Flag.JSON != "" { // parse version,destination from json logging optimization.
   176  		logopt.LogJsonOption(base.Flag.JSON)
   177  	}
   178  
   179  	ir.EscFmt = escape.Fmt
   180  	ir.IsIntrinsicCall = ssagen.IsIntrinsicCall
   181  	inline.SSADumpInline = ssagen.DumpInline
   182  	ssagen.InitEnv()
   183  	ssagen.InitTables()
   184  
   185  	types.PtrSize = ssagen.Arch.LinkArch.PtrSize
   186  	types.RegSize = ssagen.Arch.LinkArch.RegSize
   187  	types.MaxWidth = ssagen.Arch.MAXWIDTH
   188  
   189  	typecheck.Target = new(ir.Package)
   190  
   191  	base.AutogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
   192  
   193  	typecheck.InitUniverse()
   194  	typecheck.InitRuntime()
   195  	rttype.Init()
   196  
   197  	// Parse and typecheck input.
   198  	noder.LoadPackage(flag.Args())
   199  
   200  	// As a convenience to users (toolchain maintainers, in particular),
   201  	// when compiling a package named "main", we default the package
   202  	// path to "main" if the -p flag was not specified.
   203  	if base.Ctxt.Pkgpath == obj.UnlinkablePkg && types.LocalPkg.Name == "main" {
   204  		base.Ctxt.Pkgpath = "main"
   205  		types.LocalPkg.Path = "main"
   206  		types.LocalPkg.Prefix = "main"
   207  	}
   208  
   209  	dwarfgen.RecordPackageName()
   210  
   211  	// Prepare for backend processing.
   212  	ssagen.InitConfig()
   213  
   214  	// Apply coverage fixups, if applicable.
   215  	coverage.Fixup()
   216  
   217  	// Read profile file and build profile-graph and weighted-call-graph.
   218  	base.Timer.Start("fe", "pgo-load-profile")
   219  	var profile *pgo.Profile
   220  	if base.Flag.PgoProfile != "" {
   221  		var err error
   222  		profile, err = pgo.New(base.Flag.PgoProfile)
   223  		if err != nil {
   224  			log.Fatalf("%s: PGO error: %v", base.Flag.PgoProfile, err)
   225  		}
   226  	}
   227  
   228  	// Interleaved devirtualization and inlining.
   229  	base.Timer.Start("fe", "devirtualize-and-inline")
   230  	interleaved.DevirtualizeAndInlinePackage(typecheck.Target, profile)
   231  
   232  	noder.MakeWrappers(typecheck.Target) // must happen after inlining
   233  
   234  	// Get variable capture right in for loops.
   235  	var transformed []loopvar.VarAndLoop
   236  	for _, fn := range typecheck.Target.Funcs {
   237  		transformed = append(transformed, loopvar.ForCapture(fn)...)
   238  	}
   239  	ir.CurFunc = nil
   240  
   241  	// Build init task, if needed.
   242  	pkginit.MakeTask()
   243  
   244  	// Generate ABI wrappers. Must happen before escape analysis
   245  	// and doesn't benefit from dead-coding or inlining.
   246  	symABIs.GenABIWrappers()
   247  
   248  	// Escape analysis.
   249  	// Required for moving heap allocations onto stack,
   250  	// which in turn is required by the closure implementation,
   251  	// which stores the addresses of stack variables into the closure.
   252  	// If the closure does not escape, it needs to be on the stack
   253  	// or else the stack copier will not update it.
   254  	// Large values are also moved off stack in escape analysis;
   255  	// because large values may contain pointers, it must happen early.
   256  	base.Timer.Start("fe", "escapes")
   257  	escape.Funcs(typecheck.Target.Funcs)
   258  
   259  	loopvar.LogTransformations(transformed)
   260  
   261  	// Collect information for go:nowritebarrierrec
   262  	// checking. This must happen before transforming closures during Walk
   263  	// We'll do the final check after write barriers are
   264  	// inserted.
   265  	if base.Flag.CompilingRuntime {
   266  		ssagen.EnableNoWriteBarrierRecCheck()
   267  	}
   268  
   269  	ir.CurFunc = nil
   270  
   271  	reflectdata.WriteBasicTypes()
   272  
   273  	// Compile top-level declarations.
   274  	//
   275  	// There are cyclic dependencies between all of these phases, so we
   276  	// need to iterate all of them until we reach a fixed point.
   277  	base.Timer.Start("be", "compilefuncs")
   278  	for nextFunc, nextExtern := 0, 0; ; {
   279  		reflectdata.WriteRuntimeTypes()
   280  
   281  		if nextExtern < len(typecheck.Target.Externs) {
   282  			switch n := typecheck.Target.Externs[nextExtern]; n.Op() {
   283  			case ir.ONAME:
   284  				dumpGlobal(n)
   285  			case ir.OLITERAL:
   286  				dumpGlobalConst(n)
   287  			case ir.OTYPE:
   288  				reflectdata.NeedRuntimeType(n.Type())
   289  			}
   290  			nextExtern++
   291  			continue
   292  		}
   293  
   294  		if nextFunc < len(typecheck.Target.Funcs) {
   295  			enqueueFunc(typecheck.Target.Funcs[nextFunc])
   296  			nextFunc++
   297  			continue
   298  		}
   299  
   300  		// The SSA backend supports using multiple goroutines, so keep it
   301  		// as late as possible to maximize how much work we can batch and
   302  		// process concurrently.
   303  		if len(compilequeue) != 0 {
   304  			compileFunctions()
   305  			continue
   306  		}
   307  
   308  		// Finalize DWARF inline routine DIEs, then explicitly turn off
   309  		// further DWARF inlining generation to avoid problems with
   310  		// generated method wrappers.
   311  		//
   312  		// Note: The DWARF fixup code for inlined calls currently doesn't
   313  		// allow multiple invocations, so we intentionally run it just
   314  		// once after everything else. Worst case, some generated
   315  		// functions have slightly larger DWARF DIEs.
   316  		if base.Ctxt.DwFixups != nil {
   317  			base.Ctxt.DwFixups.Finalize(base.Ctxt.Pkgpath, base.Debug.DwarfInl != 0)
   318  			base.Ctxt.DwFixups = nil
   319  			base.Flag.GenDwarfInl = 0
   320  			continue // may have called reflectdata.TypeLinksym (#62156)
   321  		}
   322  
   323  		break
   324  	}
   325  
   326  	base.Timer.AddEvent(int64(len(typecheck.Target.Funcs)), "funcs")
   327  
   328  	if base.Flag.CompilingRuntime {
   329  		// Write barriers are now known. Check the call graph.
   330  		ssagen.NoWriteBarrierRecCheck()
   331  	}
   332  
   333  	// Add keep relocations for global maps.
   334  	if base.Debug.WrapGlobalMapCtl != 1 {
   335  		staticinit.AddKeepRelocations()
   336  	}
   337  
   338  	// Write object data to disk.
   339  	base.Timer.Start("be", "dumpobj")
   340  	dumpdata()
   341  	base.Ctxt.NumberSyms()
   342  	dumpobj()
   343  	if base.Flag.AsmHdr != "" {
   344  		dumpasmhdr()
   345  	}
   346  
   347  	ssagen.CheckLargeStacks()
   348  	typecheck.CheckFuncStack()
   349  
   350  	if len(compilequeue) != 0 {
   351  		base.Fatalf("%d uncompiled functions", len(compilequeue))
   352  	}
   353  
   354  	logopt.FlushLoggedOpts(base.Ctxt, base.Ctxt.Pkgpath)
   355  	base.ExitIfErrors()
   356  
   357  	base.FlushErrors()
   358  	base.Timer.Stop()
   359  
   360  	if base.Flag.Bench != "" {
   361  		if err := writebench(base.Flag.Bench); err != nil {
   362  			log.Fatalf("cannot write benchmark data: %v", err)
   363  		}
   364  	}
   365  }
   366  
   367  func writebench(filename string) error {
   368  	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
   369  	if err != nil {
   370  		return err
   371  	}
   372  
   373  	var buf bytes.Buffer
   374  	fmt.Fprintln(&buf, "commit:", buildcfg.Version)
   375  	fmt.Fprintln(&buf, "goos:", runtime.GOOS)
   376  	fmt.Fprintln(&buf, "goarch:", runtime.GOARCH)
   377  	base.Timer.Write(&buf, "BenchmarkCompile:"+base.Ctxt.Pkgpath+":")
   378  
   379  	n, err := f.Write(buf.Bytes())
   380  	if err != nil {
   381  		return err
   382  	}
   383  	if n != buf.Len() {
   384  		panic("bad writer")
   385  	}
   386  
   387  	return f.Close()
   388  }
   389  
   390  func makePos(b *src.PosBase, line, col uint) src.XPos {
   391  	return base.Ctxt.PosTable.XPos(src.MakePos(b, line, col))
   392  }