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