| // 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 !ln.Left.isBlank() || !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 == 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) | |
| } | |
| // (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) | |
| 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) | |
| } | |
| } |