]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: speedup large init function compile time
authorCuong Manh Le <cuong.manhle.vn@gmail.com>
Wed, 21 Jan 2026 11:11:29 +0000 (18:11 +0700)
committerGopher Robot <gobot@golang.org>
Thu, 22 Jan 2026 20:55:00 +0000 (12:55 -0800)
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>

src/cmd/compile/internal/noder/reader.go
src/cmd/compile/internal/pkginit/init.go
src/cmd/compile/internal/staticinit/sched.go

index d7dd58d8caafe8beddda21690f4e30622811853f..7f82cfc6bac9937693d3b2c52cdae1dec6ed7351 100644 (file)
@@ -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)
 }
 
index 04f6a5d3509f85fee8d9c453ab51a3b8bee2fb63..9be15860a664da2fb12ab557854f161b8110e9e4 100644 (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),
index c79715be4699c26d30e37438f4e1ddaf4344e8eb..d7ad8ee1c8ccf0092caba9a356503bcef94dd884 100644 (file)
@@ -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
+}