mirror of
https://github.com/golang/go.git
synced 2026-02-07 03:15:05 +03:00
cmd/compile: speedup large init function compile time
Fixes #77153 Change-Id: Ia3906e4d686281be78b65daf7a7a4fd1b2b2483d Reviewed-on: https://go-review.googlesource.com/c/go/+/737880 Reviewed-by: Keith Randall <khr@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Carlos Amedee <carlos@golang.org> Reviewed-by: Keith Randall <khr@golang.org> Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
committed by
Gopher Robot
parent
e9e05687de
commit
1b7e5836ad
@@ -3342,6 +3342,9 @@ func (r *reader) pkgInitOrder(target *ir.Package) {
|
||||
// Outline (if legal/profitable) global map inits.
|
||||
staticinit.OutlineMapInits(fn)
|
||||
|
||||
// Split large init function.
|
||||
staticinit.SplitLargeInit(fn)
|
||||
|
||||
target.Inits = append(target.Inits, fn)
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
@@ -1245,3 +1246,54 @@ func OutlineMapInits(fn *ir.Func) {
|
||||
fmt.Fprintf(os.Stderr, "=-= outlined %v map initializations\n", outlined)
|
||||
}
|
||||
}
|
||||
|
||||
const maxInitStatements = 1000
|
||||
|
||||
// SplitLargeInit breaks up a large "init" function into smaller chunks to avoid slow compilation.
|
||||
func SplitLargeInit(fn *ir.Func) {
|
||||
if !fn.IsPackageInit() || len(fn.Body) <= maxInitStatements {
|
||||
return
|
||||
}
|
||||
var calls []ir.Node
|
||||
for chunk := range slices.Chunk(fn.Body, maxInitStatements) {
|
||||
varInitFn := generateVarInitFunc(chunk)
|
||||
ir.WithFunc(fn, func() {
|
||||
calls = append(calls, typecheck.Call(varInitFn.Pos(), varInitFn.Nname, nil, false))
|
||||
})
|
||||
}
|
||||
fn.Body = calls
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// varInitGen is a counter used to uniquify compiler-generated functions for initializing variables.
|
||||
var varInitGen int
|
||||
|
||||
const varInitFuncPrefix = "init.var."
|
||||
|
||||
// Create a new function that will (eventually) have this form:
|
||||
//
|
||||
// func init.var.%d() {
|
||||
// ...
|
||||
// }
|
||||
func generateVarInitFunc(body []ir.Node) *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.
|
||||
typecheck.DeclFunc(fn)
|
||||
|
||||
fn.Body = body
|
||||
typecheck.FinishFuncBody()
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user