import (
"cmd/compile/internal/types"
+ "cmd/internal/obj"
)
// A function named init is a special case.
return s
}
-// anyinit reports whether there any interesting init statements.
-func anyinit(n []*Node) bool {
- for _, ln := range n {
- switch ln.Op {
- case ODCLFUNC, ODCLCONST, ODCLTYPE, OEMPTY:
- case OAS:
- if !ln.Left.isBlank() || !candiscard(ln.Right) {
- return true
- }
- default:
- return true
- }
- }
-
- // is this main
- if localpkg.Name == "main" {
- return true
- }
+// fninit makes 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 fninit(n []*Node) {
+ nf := initfix(n)
- // is there an explicit init function
- if renameinitgen > 0 {
- return true
- }
+ var deps []*obj.LSym // initTask records for packages the current package depends on
+ var fns []*obj.LSym // functions to call for package initialization
- // are there any imported init functions
- for _, s := range types.InitSyms {
- if s.Def != nil {
- return true
+ // Find imported packages with init tasks.
+ for _, p := range types.ImportedPkgList() {
+ if s, ok := p.LookupOK(".inittask"); ok {
+ deps = append(deps, s.Linksym())
}
}
- // then none
- return false
-}
-
-// fninit hand-crafts package initialization code.
-//
-// func init.ializers() { (0)
-// <init stmts>
-// }
-// var initdone· uint8 (1)
-// func init() { (2)
-// if initdone· > 1 { (3)
-// return (3a)
-// }
-// if initdone· == 1 { (4)
-// throw() (4a)
-// }
-// initdone· = 1 (5)
-// // over all matching imported symbols
-// <pkg>.init() (6)
-// init.ializers() (7)
-// init.<n>() // if any (8)
-// initdone· = 2 (9)
-// return (10)
-// }
-func fninit(n []*Node) {
- lineno = autogeneratedPos
- nf := initfix(n)
- if !anyinit(nf) {
- return
- }
-
- // (0)
// Make a function that contains all the initialization statements.
- // This is a separate function because we want it to appear in
- // stack traces, where the init function itself does not.
- var initializers *types.Sym
if len(nf) > 0 {
lineno = nf[0].Pos // prolog/epilog gets line number of first init stmt
- initializers = lookup("init.ializers")
+ initializers := lookup("init.ializers")
disableExport(initializers)
fn := dclfunc(initializers, nod(OTFUNC, nil, nil))
for _, dcl := range dummyInitFn.Func.Dcl {
typecheckslice(nf, ctxStmt)
Curfn = nil
funccompile(fn)
- lineno = autogeneratedPos
+ fns = append(fns, initializers.Linksym())
}
if dummyInitFn.Func.Dcl != nil {
// We only generate temps using dummyInitFn if there
Fatalf("dummyInitFn still has declarations")
}
- var r []*Node
-
- // (1)
- gatevar := newname(lookup("initdone·"))
- addvar(gatevar, types.Types[TUINT8], PEXTERN)
-
- // (2)
- initsym := lookup("init")
- fn := dclfunc(initsym, nod(OTFUNC, nil, nil))
-
- // (3)
- a := nod(OIF, nil, nil)
- a.Left = nod(OGT, gatevar, nodintconst(1))
- a.SetLikely(true)
- r = append(r, a)
- // (3a)
- a.Nbody.Set1(nod(ORETURN, nil, nil))
-
- // (4)
- b := nod(OIF, nil, nil)
- b.Left = nod(OEQ, gatevar, nodintconst(1))
- // this actually isn't likely, but code layout is better
- // like this: no JMP needed after the call.
- b.SetLikely(true)
- r = append(r, b)
- // (4a)
- b.Nbody.Set1(nod(OCALL, syslook("throwinit"), nil))
-
- // (5)
- a = nod(OAS, gatevar, nodintconst(1))
-
- r = append(r, a)
-
- // (6)
- for _, s := range types.InitSyms {
- if s == initsym {
- continue
- }
- n := resolve(oldname(s))
- if n.Op == ONONAME {
- // No package-scope init function; just a
- // local variable, field name, or something.
- continue
- }
- n.checkInitFuncSignature()
- a = nod(OCALL, n, nil)
- r = append(r, a)
+ // Record user init functions.
+ for i := 0; i < renameinitgen; i++ {
+ s := lookupN("init.", i)
+ fns = append(fns, s.Linksym())
}
- // (7)
- if initializers != nil {
- n := newname(initializers)
- addvar(n, functype(nil, nil, nil), PFUNC)
- r = append(r, nod(OCALL, n, nil))
+ if len(deps) == 0 && len(fns) == 0 && localpkg.Name != "main" && localpkg.Name != "runtime" {
+ return // nothing to initialize
}
- // (8)
-
- // maxInlineInitCalls is the threshold at which we switch
- // from generating calls inline to generating a static array
- // of functions and calling them in a loop.
- // See CL 41500 for more discussion.
- const maxInlineInitCalls = 500
-
- if renameinitgen < maxInlineInitCalls {
- // Not many init functions. Just call them all directly.
- for i := 0; i < renameinitgen; i++ {
- s := lookupN("init.", i)
- n := asNode(s.Def)
- n.checkInitFuncSignature()
- a = nod(OCALL, n, nil)
- r = append(r, a)
- }
- } else {
- // Lots of init functions.
- // Set up an array of functions and loop to call them.
- // This is faster to compile and similar at runtime.
-
- // Build type [renameinitgen]func().
- typ := types.NewArray(functype(nil, nil, nil), int64(renameinitgen))
-
- // Make and fill array.
- fnarr := staticname(typ)
- fnarr.Name.SetReadonly(true)
- for i := 0; i < renameinitgen; i++ {
- s := lookupN("init.", i)
- lhs := nod(OINDEX, fnarr, nodintconst(int64(i)))
- rhs := asNode(s.Def)
- rhs.checkInitFuncSignature()
- as := nod(OAS, lhs, rhs)
- as = typecheck(as, ctxStmt)
- genAsStatic(as)
- }
-
- // Generate a loop that calls each function in turn.
- // for i := 0; i < renameinitgen; i++ {
- // fnarr[i]()
- // }
- i := temp(types.Types[TINT])
- fnidx := nod(OINDEX, fnarr, i)
- fnidx.SetBounded(true)
-
- zero := nod(OAS, i, nodintconst(0))
- cond := nod(OLT, i, nodintconst(int64(renameinitgen)))
- incr := nod(OAS, i, nod(OADD, i, nodintconst(1)))
- body := nod(OCALL, fnidx, nil)
-
- loop := nod(OFOR, cond, incr)
- loop.Nbody.Set1(body)
- loop.Ninit.Set1(zero)
-
- loop = typecheck(loop, ctxStmt)
- r = append(r, loop)
+ // Make an .inittask structure.
+ sym := lookup(".inittask")
+ nn := newname(sym)
+ nn.Type = types.Types[TUINT8] // dummy type
+ nn.SetClass(PEXTERN)
+ sym.Def = asTypesNode(nn)
+ exportsym(nn)
+ lsym := sym.Linksym()
+ ot := 0
+ ot = duintptr(lsym, ot, 0) // state: not initialized yet
+ ot = duintptr(lsym, ot, uint64(len(deps)))
+ ot = duintptr(lsym, ot, uint64(len(fns)))
+ for _, d := range deps {
+ ot = dsymptr(lsym, ot, d, 0)
}
-
- // (9)
- a = nod(OAS, gatevar, nodintconst(2))
-
- r = append(r, a)
-
- // (10)
- a = nod(ORETURN, nil, nil)
-
- r = append(r, a)
- exportsym(fn.Func.Nname)
-
- fn.Nbody.Set(r)
- funcbody()
-
- Curfn = fn
- fn = typecheck(fn, ctxStmt)
- typecheckslice(r, ctxStmt)
- Curfn = nil
- funccompile(fn)
+ for _, f := range fns {
+ ot = dsymptr(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.
+ ggloblsym(lsym, int32(ot), obj.NOPTR)
}
func (n *Node) checkInitFuncSignature() {
raceprocctx0 uintptr
)
-//go:linkname runtime_init runtime.init
-func runtime_init()
+//go:linkname runtime_inittask runtime..inittask
+var runtime_inittask initTask
-//go:linkname main_init main.init
-func main_init()
+//go:linkname main_inittask main..inittask
+var main_inittask initTask
// main_init_done is a signal used by cgocallbackg that initialization
// has been completed. It is made before _cgo_notify_runtime_init_done,
throw("runtime.main not on m0")
}
- runtime_init() // must be before defer
+ doInit(&runtime_inittask) // must be before defer
if nanotime() == 0 {
throw("nanotime returning zero")
}
cgocall(_cgo_notify_runtime_init_done, nil)
}
- fn := main_init // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
- fn()
+ doInit(&main_inittask)
+
close(main_init_done)
needUnlock = false
// has a main, but it is not executed.
return
}
- fn = main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
+ fn := main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
fn()
if raceenabled {
racefini()
}
return a
}
+
+// An initTask represents the set of initializations that need to be done for a package.
+type initTask struct {
+ // TODO: pack the first 3 fields more tightly?
+ state uintptr // 0 = uninitialized, 1 = in progress, 2 = done
+ ndeps uintptr
+ nfns uintptr
+ // followed by ndeps instances of an *initTask, one per package depended on
+ // followed by nfns pcs, one per init function to run
+}
+
+func doInit(t *initTask) {
+ switch t.state {
+ case 2: // fully initialized
+ return
+ case 1: // initialization in progress
+ throw("recursive call during initialization - linker skew")
+ default: // not initialized yet
+ t.state = 1 // initialization in progress
+ for i := uintptr(0); i < t.ndeps; i++ {
+ p := add(unsafe.Pointer(t), (3+i)*sys.PtrSize)
+ t2 := *(**initTask)(p)
+ doInit(t2)
+ }
+ for i := uintptr(0); i < t.nfns; i++ {
+ p := add(unsafe.Pointer(t), (3+t.ndeps+i)*sys.PtrSize)
+ f := *(*func())(unsafe.Pointer(&p))
+ f()
+ }
+ t.state = 2 // initialization done
+ }
+}