mirror of https://github.com/golang/go.git
151 lines
3.8 KiB
Go
151 lines
3.8 KiB
Go
// 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/ssagen"
|
|
"cmd/compile/internal/typecheck"
|
|
"cmd/compile/internal/types"
|
|
"cmd/compile/internal/walk"
|
|
)
|
|
|
|
// "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()) // TODO register args; remove this once all is done by abiutils
|
|
a := ssagen.AbiForFunc(fn)
|
|
abiInfo := a.ABIAnalyze(fn.Type(), true) // will set parameter spill/home locations correctly
|
|
liveness.WriteFuncMap(fn, abiInfo)
|
|
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)
|
|
})
|
|
}
|
|
|
|
// We queue up a goroutine per function that needs to be
|
|
// compiled, but require them to grab an available worker ID
|
|
// before doing any substantial work to limit parallelism.
|
|
workerIDs := make(chan int, base.Flag.LowerC)
|
|
for i := 0; i < base.Flag.LowerC; i++ {
|
|
workerIDs <- i
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
var asyncCompile func(*ir.Func)
|
|
asyncCompile = func(fn *ir.Func) {
|
|
wg.Add(1)
|
|
go func() {
|
|
worker := <-workerIDs
|
|
ssagen.Compile(fn, worker)
|
|
workerIDs <- worker
|
|
|
|
// Done compiling fn. Schedule it's closures for compilation.
|
|
for _, closure := range fn.Closures {
|
|
asyncCompile(closure)
|
|
}
|
|
wg.Done()
|
|
}()
|
|
}
|
|
|
|
types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
|
|
base.Ctxt.InParallel = true
|
|
for _, fn := range compilequeue {
|
|
asyncCompile(fn)
|
|
}
|
|
compilequeue = nil
|
|
wg.Wait()
|
|
base.Ctxt.InParallel = false
|
|
types.CalcSizeDisabled = false
|
|
}
|