summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/pkginit/init.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/pkginit/init.go')
-rw-r--r--src/cmd/compile/internal/pkginit/init.go144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/pkginit/init.go b/src/cmd/compile/internal/pkginit/init.go
new file mode 100644
index 0000000..40f1408
--- /dev/null
+++ b/src/cmd/compile/internal/pkginit/init.go
@@ -0,0 +1,144 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pkginit
+
+import (
+ "cmd/compile/internal/base"
+ "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:
+// 1) Initialize all of the packages the current package depends on.
+// 2) Initialize all the variables that have initializers.
+// 3) Run any init functions.
+func Task() *ir.Name {
+ var deps []*obj.LSym // initTask records for packages the current package depends on
+ var fns []*obj.LSym // functions to call for package initialization
+
+ // Find imported packages with init tasks.
+ for _, pkg := range typecheck.Target.Imports {
+ n := typecheck.Resolve(ir.NewIdent(base.Pos, pkg.Lookup(".inittask")))
+ if n.Op() == ir.ONONAME {
+ continue
+ }
+ if n.Op() != ir.ONAME || n.(*ir.Name).Class != ir.PEXTERN {
+ base.Fatalf("bad inittask: %v", n)
+ }
+ deps = append(deps, n.(*ir.Name).Linksym())
+ }
+
+ // 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.
+ 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 {
+ if stmt := fn.Body[0]; stmt.Op() == ir.OBLOCK && len(stmt.(*ir.BlockStmt).List) == 0 {
+ continue
+ }
+ }
+ fns = append(fns, fn.Nname.Linksym())
+ }
+
+ if len(deps) == 0 && len(fns) == 0 && types.LocalPkg.Name != "main" && types.LocalPkg.Name != "runtime" {
+ return nil // nothing to initialize
+ }
+
+ // Make an .inittask structure.
+ sym := typecheck.Lookup(".inittask")
+ task := typecheck.NewName(sym)
+ task.SetType(types.Types[types.TUINT8]) // fake type
+ task.Class = ir.PEXTERN
+ sym.Def = task
+ lsym := task.Linksym()
+ ot := 0
+ ot = objw.Uintptr(lsym, ot, 0) // state: not initialized yet
+ ot = objw.Uintptr(lsym, ot, uint64(len(deps)))
+ ot = objw.Uintptr(lsym, ot, uint64(len(fns)))
+ for _, d := range deps {
+ ot = objw.SymPtr(lsym, ot, d, 0)
+ }
+ for _, f := range fns {
+ ot = objw.SymPtr(lsym, ot, f, 0)
+ }
+ // An initTask has pointers, but none into the Go heap.
+ // It's not quite read only, the state field must be modifiable.
+ objw.Global(lsym, int32(ot), obj.NOPTR)
+ return task
+}