// 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 gc import ( "cmd/compile/internal/types" ) // A function named init is a special case. // It is called by the initialization before main is run. // To make it unique within a package and also uncallable, // the name, normally "pkg.init", is altered to "pkg.init.0". var renameinitgen int func renameinit() *types.Sym { s := lookupN("init.", renameinitgen) renameinitgen++ 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 !isblank(ln.Left) || !candiscard(ln.Right) { return true } default: return true } } // is this main if localpkg.Name == "main" { return true } // is there an explicit init function if renameinitgen > 0 { return true } // are there any imported init functions for _, s := range types.InitSyms { if s.Def != nil { return true } } // then none return false } // fninit hand-crafts package initialization code. // // 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 stmts> } (7) // init.<n>() // if any (8) // initdone· = 2 (9) // return (10) // } func fninit(n []*Node) { lineno = autogeneratedPos nf := initfix(n) if !anyinit(nf) { return } 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.Def != nil && s != initsym { n := asNode(s.Def) n.checkInitFuncSignature() a = nod(OCALL, n, nil) r = append(r, a) } } // (7) r = append(r, nf...) // (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, Etop) 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, Etop) loop = walkstmt(loop) r = append(r, loop) } // (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, Etop) typecheckslice(r, Etop) Curfn = nil funccompile(fn) } func (n *Node) checkInitFuncSignature() { if n.Type.NumRecvs()+n.Type.NumParams()+n.Type.NumResults() > 0 { Fatalf("init function cannot have receiver, params, or results: %v (%v)", n, n.Type) } }