]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/pkginit: separate "init" and "inittask" logic
authorMatthew Dempsky <mdempsky@google.com>
Tue, 31 Aug 2021 20:21:11 +0000 (13:21 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Wed, 1 Sep 2021 18:16:17 +0000 (18:16 +0000)
This CL splits the creation of the "init" function responsible for
executing package-scope variable initialization statemens from the
creation of the "inittask" record that tells the runtime how to
sequence all program-wide package initialization.

Longer term, this is desirable because sorting variable initialization
is already handled by types2 (with Info.InitOrder), so we might as
well reuse that.

As a more immediate impetus, for unified IR, I want to defer method
wrapper generation until after inlining (to know which wrappers are
needed). But the staticinit optimization used to decide whether to
emit the inittask calls into reflectdata, which in turn tries to
generate its own method wrappers. So separating the work allows to
create the "init" function early and then emit "inittask" after
inlining is done.

Change-Id: Ice1d421f92feecaaeafdf7da6b9647c0f27e3571
Reviewed-on: https://go-review.googlesource.com/c/go/+/346629
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
src/cmd/compile/internal/gc/main.go
src/cmd/compile/internal/pkginit/init.go
src/cmd/compile/internal/pkginit/initorder.go

index 8a365f8f6a1f64c40ce48afcaf28f48c18840bc4..0dbe47f6537e51cd45b97aa0e8007efb4b0f71ca 100644 (file)
@@ -195,18 +195,19 @@ func Main(archInit func(*ssagen.ArchInfo)) {
        // because it generates itabs for initializing global variables.
        ssagen.InitConfig()
 
-       // Build init task.
-       if initTask := pkginit.Task(); initTask != nil {
-               typecheck.Export(initTask)
-       }
+       // Create "init" function for package-scope variable initialization
+       // statements, if any.
+       //
+       // Note: This needs to happen early, before any optimizations. The
+       // Go spec defines a precise order than initialization should be
+       // carried out in, and even mundane optimizations like dead code
+       // removal can skew the results (e.g., #43444).
+       pkginit.MakeInit()
 
        // Stability quirk: sort top-level declarations, so we're not
        // sensitive to the order that functions are added. In particular,
        // the order that noder+typecheck add function closures is very
        // subtle, and not important to reproduce.
-       //
-       // Note: This needs to happen after pkginit.Task, otherwise it risks
-       // changing the order in which top-level variables are initialized.
        if base.Debug.UnifiedQuirks != 0 {
                s := typecheck.Target.Decls
                sort.SliceStable(s, func(i, j int) bool {
@@ -253,6 +254,11 @@ func Main(archInit func(*ssagen.ArchInfo)) {
        }
        ir.CurFunc = nil
 
+       // Build init task, if needed.
+       if initTask := pkginit.Task(); initTask != nil {
+               typecheck.Export(initTask)
+       }
+
        // Generate ABI wrappers. Must happen before escape analysis
        // and doesn't benefit from dead-coding or inlining.
        symABIs.GenABIWrappers()
index 7cad2622146d0e580cb257348ec234ecb05ba713..40f14082600c3d1515c3d1e9350d961f4dbcb77e 100644 (file)
@@ -6,14 +6,62 @@ package pkginit
 
 import (
        "cmd/compile/internal/base"
-       "cmd/compile/internal/deadcode"
        "cmd/compile/internal/ir"
        "cmd/compile/internal/objw"
+       "cmd/compile/internal/staticinit"
        "cmd/compile/internal/typecheck"
        "cmd/compile/internal/types"
        "cmd/internal/obj"
+       "cmd/internal/src"
 )
 
+// MakeInit creates a synthetic init function to handle any
+// package-scope initialization statements.
+//
+// TODO(mdempsky): Move into noder, so that the types2-based frontends
+// can use Info.InitOrder instead.
+func MakeInit() {
+       nf := initOrder(typecheck.Target.Decls)
+       if len(nf) == 0 {
+               return
+       }
+
+       // Make a function that contains all the initialization statements.
+       base.Pos = nf[0].Pos() // prolog/epilog gets line number of first init stmt
+       initializers := typecheck.Lookup("init")
+       fn := typecheck.DeclFunc(initializers, ir.NewFuncType(base.Pos, nil, nil, nil))
+       for _, dcl := range typecheck.InitTodoFunc.Dcl {
+               dcl.Curfn = fn
+       }
+       fn.Dcl = append(fn.Dcl, typecheck.InitTodoFunc.Dcl...)
+       typecheck.InitTodoFunc.Dcl = nil
+
+       // Suppress useless "can inline" diagnostics.
+       // Init functions are only called dynamically.
+       fn.SetInlinabilityChecked(true)
+
+       fn.Body = nf
+       typecheck.FinishFuncBody()
+
+       typecheck.Func(fn)
+       ir.WithFunc(fn, func() {
+               typecheck.Stmts(nf)
+       })
+       typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
+
+       // Prepend to Inits, so it runs first, before any user-declared init
+       // functions.
+       typecheck.Target.Inits = append([]*ir.Func{fn}, typecheck.Target.Inits...)
+
+       if typecheck.InitTodoFunc.Dcl != nil {
+               // We only generate temps using InitTodoFunc if there
+               // are package-scope initialization statements, so
+               // something's weird if we get here.
+               base.Fatalf("InitTodoFunc still has declarations")
+       }
+       typecheck.InitTodoFunc = nil
+}
+
 // Task makes and returns an initialization record for the package.
 // See runtime/proc.go:initTask for its layout.
 // The 3 tasks for initialization are:
@@ -21,8 +69,6 @@ import (
 //   2) Initialize all the variables that have initializers.
 //   3) Run any init functions.
 func Task() *ir.Name {
-       nf := initOrder(typecheck.Target.Decls)
-
        var deps []*obj.LSym // initTask records for packages the current package depends on
        var fns []*obj.LSym  // functions to call for package initialization
 
@@ -38,39 +84,28 @@ func Task() *ir.Name {
                deps = append(deps, n.(*ir.Name).Linksym())
        }
 
-       // Make a function that contains all the initialization statements.
-       if len(nf) > 0 {
-               base.Pos = nf[0].Pos() // prolog/epilog gets line number of first init stmt
-               initializers := typecheck.Lookup("init")
-               fn := typecheck.DeclFunc(initializers, ir.NewFuncType(base.Pos, nil, nil, nil))
-               for _, dcl := range typecheck.InitTodoFunc.Dcl {
-                       dcl.Curfn = fn
-               }
-               fn.Dcl = append(fn.Dcl, typecheck.InitTodoFunc.Dcl...)
-               typecheck.InitTodoFunc.Dcl = nil
-
-               fn.Body = nf
-               typecheck.FinishFuncBody()
-
-               typecheck.Func(fn)
-               ir.CurFunc = fn
-               typecheck.Stmts(nf)
-               ir.CurFunc = nil
-               typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
-               fns = append(fns, fn.Linksym())
-       }
-       if typecheck.InitTodoFunc.Dcl != nil {
-               // We only generate temps using InitTodoFunc if there
-               // are package-scope initialization statements, so
-               // something's weird if we get here.
-               base.Fatalf("InitTodoFunc still has declarations")
-       }
-       typecheck.InitTodoFunc = nil
-
        // Record user init functions.
        for _, fn := range typecheck.Target.Inits {
-               // Must happen after initOrder; see #43444.
-               deadcode.Func(fn)
+               if fn.Sym().Name == "init" {
+                       // Synthetic init function for initialization of package-scope
+                       // variables. We can use staticinit to optimize away static
+                       // assignments.
+                       s := staticinit.Schedule{
+                               Plans: make(map[ir.Node]*staticinit.Plan),
+                               Temps: make(map[ir.Node]*ir.Name),
+                       }
+                       for _, n := range fn.Body {
+                               s.StaticInit(n)
+                       }
+                       fn.Body = s.Out
+                       ir.WithFunc(fn, func() {
+                               typecheck.Stmts(fn.Body)
+                       })
+
+                       if len(fn.Body) == 0 {
+                               fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)}
+                       }
+               }
 
                // Skip init functions with empty bodies.
                if len(fn.Body) == 1 {
index 0aad63a69f6b05fa6c9a41a5c5fb65d9cf092d16..a50975343fbced39fdd911c769575e0ceff8edb1 100644 (file)
@@ -11,7 +11,6 @@ import (
 
        "cmd/compile/internal/base"
        "cmd/compile/internal/ir"
-       "cmd/compile/internal/staticinit"
 )
 
 // Package initialization
@@ -78,10 +77,7 @@ type InitOrder struct {
 // corresponding list of statements to include in the init() function
 // body.
 func initOrder(l []ir.Node) []ir.Node {
-       s := staticinit.Schedule{
-               Plans: make(map[ir.Node]*staticinit.Plan),
-               Temps: make(map[ir.Node]*ir.Name),
-       }
+       var res ir.Nodes
        o := InitOrder{
                blocking: make(map[ir.Node][]ir.Node),
                order:    make(map[ir.Node]int),
@@ -92,7 +88,7 @@ func initOrder(l []ir.Node) []ir.Node {
                switch n.Op() {
                case ir.OAS, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
                        o.processAssign(n)
-                       o.flushReady(s.StaticInit)
+                       o.flushReady(func(n ir.Node) { res.Append(n) })
                case ir.ODCLCONST, ir.ODCLFUNC, ir.ODCLTYPE:
                        // nop
                default:
@@ -125,7 +121,7 @@ func initOrder(l []ir.Node) []ir.Node {
                base.Fatalf("expected empty map: %v", o.blocking)
        }
 
-       return s.Out
+       return res
 }
 
 func (o *InitOrder) processAssign(n ir.Node) {