mirror of
https://github.com/golang/go.git
synced 2026-08-02 20:44:20 +08:00
cmd/compile: speedup large synthetic init function compile time
This CL redo the work in CL 737880 to split large synthetic init function into smaller ones, allow faster compile time for package with many global initialization statements. Updates #80141 Fixes #77153 Change-Id: I8475b6c7ca8bcf997baf043986ba639d897c2510 Reviewed-on: https://go-review.googlesource.com/c/go/+/795261 LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Russ Cox <rsc@golang.org> Reviewed-by: Keith Randall <khr@google.com>
This commit is contained in:
parent
c1657c45eb
commit
dc72b71db0
@ -11,6 +11,7 @@ import (
|
||||
"internal/buildcfg"
|
||||
"internal/pkgbits"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
@ -3542,6 +3543,51 @@ func (r *reader) pkgInitOrder(target *ir.Package) {
|
||||
typecheck.DeclFunc(fn)
|
||||
r.curfn = fn
|
||||
|
||||
var varInitFns []*ir.Func
|
||||
if len(initOrder) <= maxInitStatements {
|
||||
fn.Body = r.doPkgInitOrder(initOrder)
|
||||
} else {
|
||||
varInitFns = r.splitLargeInitOrder(initOrder)
|
||||
calls := make([]ir.Node, len(varInitFns))
|
||||
for i, varInitFn := range varInitFns {
|
||||
ir.WithFunc(fn, func() {
|
||||
calls[i] = typecheck.Call(varInitFn.Pos(), varInitFn.Nname, nil, false)
|
||||
})
|
||||
}
|
||||
fn.Body = calls
|
||||
}
|
||||
|
||||
typecheck.FinishFuncBody()
|
||||
r.curfn = nil
|
||||
r.locals = nil
|
||||
|
||||
// Outline (if legal/profitable) global map inits.
|
||||
staticinit.OutlineMapInits(fn)
|
||||
for _, varInitFn := range varInitFns {
|
||||
staticinit.OutlineMapInits(varInitFn)
|
||||
}
|
||||
|
||||
target.Inits = append(target.Inits, fn)
|
||||
target.Inits = append(target.Inits, varInitFns...)
|
||||
}
|
||||
|
||||
const maxInitStatements = 1000
|
||||
|
||||
func (r *reader) generateVarInitFunc(body []ir.Node) *ir.Func {
|
||||
fn := staticinit.GenerateVarInitFunc()
|
||||
typecheck.DeclFunc(fn)
|
||||
|
||||
old := r.curfn
|
||||
r.curfn = fn
|
||||
fn.Body = r.doPkgInitOrder(body)
|
||||
r.curfn = old
|
||||
|
||||
typecheck.FinishFuncBody()
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func (r *reader) doPkgInitOrder(initOrder []ir.Node) []ir.Node {
|
||||
for i := range initOrder {
|
||||
lhs := make([]ir.Node, r.Len())
|
||||
for j := range lhs {
|
||||
@ -3563,17 +3609,15 @@ func (r *reader) pkgInitOrder(target *ir.Package) {
|
||||
|
||||
initOrder[i] = as
|
||||
}
|
||||
return initOrder
|
||||
}
|
||||
|
||||
fn.Body = initOrder
|
||||
|
||||
typecheck.FinishFuncBody()
|
||||
r.curfn = nil
|
||||
r.locals = nil
|
||||
|
||||
// Outline (if legal/profitable) global map inits.
|
||||
staticinit.OutlineMapInits(fn)
|
||||
|
||||
target.Inits = append(target.Inits, fn)
|
||||
func (r *reader) splitLargeInitOrder(initOrder []ir.Node) []*ir.Func {
|
||||
var initFuncs []*ir.Func
|
||||
for chunk := range slices.Chunk(initOrder, maxInitStatements) {
|
||||
initFuncs = append(initFuncs, r.generateVarInitFunc(chunk))
|
||||
}
|
||||
return initFuncs
|
||||
}
|
||||
|
||||
func (r *reader) pkgDecls(target *ir.Package) {
|
||||
|
||||
@ -87,10 +87,7 @@ func MakeTask() {
|
||||
|
||||
// Record user init functions.
|
||||
for _, fn := range typecheck.Target.Inits {
|
||||
if fn.Sym().Name == "init" {
|
||||
// Synthetic init function for initialization of package-scope
|
||||
// variables. We can use staticinit to optimize away static
|
||||
// assignments.
|
||||
if staticinit.CanOptimize(fn) {
|
||||
s := staticinit.Schedule{
|
||||
Plans: make(map[ir.Node]*staticinit.Plan),
|
||||
Temps: make(map[ir.Node]*ir.Name),
|
||||
|
||||
@ -1247,3 +1247,33 @@ func OutlineMapInits(fn *ir.Func) {
|
||||
fmt.Fprintf(os.Stderr, "=-= outlined %v map initializations\n", outlined)
|
||||
}
|
||||
}
|
||||
|
||||
// varInitGen is a counter used to uniquify compiler-generated functions for initializing variables.
|
||||
var varInitGen int
|
||||
|
||||
const varInitFuncPrefix = "init.part."
|
||||
|
||||
// GenerateVarInitFunc create a new function that will (eventually) have this form:
|
||||
//
|
||||
// func init.part.%d() {
|
||||
// ...
|
||||
// }
|
||||
func GenerateVarInitFunc() *ir.Func {
|
||||
pos := base.AutogeneratedPos
|
||||
base.Pos = pos
|
||||
|
||||
sym := typecheck.LookupNum(varInitFuncPrefix, varInitGen)
|
||||
varInitGen++
|
||||
|
||||
fn := ir.NewFunc(pos, pos, sym, types.NewSignature(nil, nil, nil))
|
||||
fn.SetInlinabilityChecked(true) // suppress inlining; otherwise, we end up with giant init eventually.
|
||||
fn.SetWrapper(true) // less disruptive on backtraces.
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
// CanOptimize reports whether the given fn can be optimized for static assignments.
|
||||
func CanOptimize(fn *ir.Func) bool {
|
||||
name := fn.Sym().Name
|
||||
return name == "init" || strings.HasPrefix(name, varInitFuncPrefix)
|
||||
}
|
||||
|
||||
1016
test/fixedbugs/issue80141.go
Normal file
1016
test/fixedbugs/issue80141.go
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user