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