summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/gc/compile.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/gc/compile.go')
-rw-r--r--src/cmd/compile/internal/gc/compile.go169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/gc/compile.go b/src/cmd/compile/internal/gc/compile.go
new file mode 100644
index 0000000..0050445
--- /dev/null
+++ b/src/cmd/compile/internal/gc/compile.go
@@ -0,0 +1,169 @@
+// Copyright 2011 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 (
+ "internal/race"
+ "math/rand"
+ "sort"
+ "sync"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/liveness"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/ssagen"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/walk"
+ "cmd/internal/obj"
+)
+
+// "Portable" code generation.
+
+var (
+ compilequeue []*ir.Func // functions waiting to be compiled
+)
+
+func enqueueFunc(fn *ir.Func) {
+ if ir.CurFunc != nil {
+ base.FatalfAt(fn.Pos(), "enqueueFunc %v inside %v", fn, ir.CurFunc)
+ }
+
+ if ir.FuncName(fn) == "_" {
+ // Skip compiling blank functions.
+ // Frontend already reported any spec-mandated errors (#29870).
+ return
+ }
+
+ if clo := fn.OClosure; clo != nil && !ir.IsTrivialClosure(clo) {
+ return // we'll get this as part of its enclosing function
+ }
+
+ if len(fn.Body) == 0 {
+ // Initialize ABI wrappers if necessary.
+ ssagen.InitLSym(fn, false)
+ types.CalcSize(fn.Type())
+ a := ssagen.AbiForBodylessFuncStackMap(fn)
+ abiInfo := a.ABIAnalyzeFuncType(fn.Type().FuncType()) // abiInfo has spill/home locations for wrapper
+ liveness.WriteFuncMap(fn, abiInfo)
+ if fn.ABI == obj.ABI0 {
+ x := ssagen.EmitArgInfo(fn, abiInfo)
+ objw.Global(x, int32(len(x.P)), obj.RODATA|obj.LOCAL)
+ }
+ return
+ }
+
+ errorsBefore := base.Errors()
+
+ todo := []*ir.Func{fn}
+ for len(todo) > 0 {
+ next := todo[len(todo)-1]
+ todo = todo[:len(todo)-1]
+
+ prepareFunc(next)
+ todo = append(todo, next.Closures...)
+ }
+
+ if base.Errors() > errorsBefore {
+ return
+ }
+
+ // Enqueue just fn itself. compileFunctions will handle
+ // scheduling compilation of its closures after it's done.
+ compilequeue = append(compilequeue, fn)
+}
+
+// prepareFunc handles any remaining frontend compilation tasks that
+// aren't yet safe to perform concurrently.
+func prepareFunc(fn *ir.Func) {
+ // Set up the function's LSym early to avoid data races with the assemblers.
+ // Do this before walk, as walk needs the LSym to set attributes/relocations
+ // (e.g. in MarkTypeUsedInInterface).
+ ssagen.InitLSym(fn, true)
+
+ // Calculate parameter offsets.
+ types.CalcSize(fn.Type())
+
+ typecheck.DeclContext = ir.PAUTO
+ ir.CurFunc = fn
+ walk.Walk(fn)
+ ir.CurFunc = nil // enforce no further uses of CurFunc
+ typecheck.DeclContext = ir.PEXTERN
+}
+
+// compileFunctions compiles all functions in compilequeue.
+// It fans out nBackendWorkers to do the work
+// and waits for them to complete.
+func compileFunctions() {
+ if len(compilequeue) == 0 {
+ return
+ }
+
+ if race.Enabled {
+ // Randomize compilation order to try to shake out races.
+ tmp := make([]*ir.Func, len(compilequeue))
+ perm := rand.Perm(len(compilequeue))
+ for i, v := range perm {
+ tmp[v] = compilequeue[i]
+ }
+ copy(compilequeue, tmp)
+ } else {
+ // Compile the longest functions first,
+ // since they're most likely to be the slowest.
+ // This helps avoid stragglers.
+ sort.Slice(compilequeue, func(i, j int) bool {
+ return len(compilequeue[i].Body) > len(compilequeue[j].Body)
+ })
+ }
+
+ // By default, we perform work right away on the current goroutine
+ // as the solo worker.
+ queue := func(work func(int)) {
+ work(0)
+ }
+
+ if nWorkers := base.Flag.LowerC; nWorkers > 1 {
+ // For concurrent builds, we create a goroutine per task, but
+ // require them to hold a unique worker ID while performing work
+ // to limit parallelism.
+ workerIDs := make(chan int, nWorkers)
+ for i := 0; i < nWorkers; i++ {
+ workerIDs <- i
+ }
+
+ queue = func(work func(int)) {
+ go func() {
+ worker := <-workerIDs
+ work(worker)
+ workerIDs <- worker
+ }()
+ }
+ }
+
+ var wg sync.WaitGroup
+ var compile func([]*ir.Func)
+ compile = func(fns []*ir.Func) {
+ wg.Add(len(fns))
+ for _, fn := range fns {
+ fn := fn
+ queue(func(worker int) {
+ ssagen.Compile(fn, worker)
+ compile(fn.Closures)
+ wg.Done()
+ })
+ }
+ }
+
+ types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
+ base.Ctxt.InParallel = true
+
+ compile(compilequeue)
+ compilequeue = nil
+ wg.Wait()
+
+ base.Ctxt.InParallel = false
+ types.CalcSizeDisabled = false
+}