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:
Cuong Manh Le 2026-06-29 16:08:08 +07:00 committed by Gopher Robot
parent c1657c45eb
commit dc72b71db0
4 changed files with 1101 additions and 14 deletions

View File

@ -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) {

View File

@ -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),

View File

@ -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

File diff suppressed because it is too large Load Diff