| // 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 |
| |
| // 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.1". |
| |
| var renameinit_initgen int |
| |
| func renameinit() *Sym { |
| renameinit_initgen++ |
| return lookupN("init.", renameinit_initgen) |
| } |
| |
| // hand-craft the following 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 anyinit(n []*Node) bool { |
| // are there any interesting init statements |
| for _, ln := range n { |
| switch ln.Op { |
| case ODCLFUNC, ODCLCONST, ODCLTYPE, OEMPTY: |
| break |
| |
| case OAS, OASWB: |
| if isblank(ln.Left) && candiscard(ln.Right) { |
| break |
| } |
| fallthrough |
| default: |
| return true |
| } |
| } |
| |
| // is this main |
| if localpkg.Name == "main" { |
| return true |
| } |
| |
| // is there an explicit init function |
| s := lookup("init.1") |
| |
| if s.Def != nil { |
| return true |
| } |
| |
| // are there any imported init functions |
| for _, s := range initSyms { |
| if s.Def != nil { |
| return true |
| } |
| } |
| |
| // then none |
| return false |
| } |
| |
| func fninit(n []*Node) { |
| nf := initfix(n) |
| if !anyinit(nf) { |
| return |
| } |
| |
| var r []*Node |
| |
| // (1) |
| gatevar := newname(lookup("initdone·")) |
| addvar(gatevar, Types[TUINT8], PEXTERN) |
| |
| // (2) |
| Maxarg = 0 |
| |
| fn := nod(ODCLFUNC, nil, nil) |
| initsym := lookup("init") |
| fn.Func.Nname = newname(initsym) |
| fn.Func.Nname.Name.Defn = fn |
| fn.Func.Nname.Name.Param.Ntype = nod(OTFUNC, nil, nil) |
| declare(fn.Func.Nname, PFUNC) |
| funchdr(fn) |
| |
| // (3) |
| a := nod(OIF, nil, nil) |
| a.Left = nod(OGT, gatevar, nodintconst(1)) |
| a.Likely = 1 |
| 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.Likely = 1 |
| 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 initSyms { |
| if s.Def != nil && s != initsym { |
| // could check that it is fn of no args/returns |
| a = nod(OCALL, s.Def, nil) |
| r = append(r, a) |
| } |
| } |
| |
| // (7) |
| r = append(r, nf...) |
| |
| // (8) |
| // could check that it is fn of no args/returns |
| for i := 1; ; i++ { |
| s := lookupN("init.", i) |
| if s.Def == nil { |
| break |
| } |
| a = nod(OCALL, s.Def, nil) |
| r = append(r, a) |
| } |
| |
| // (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(fn) |
| |
| Curfn = fn |
| fn = typecheck(fn, Etop) |
| typecheckslice(r, Etop) |
| Curfn = nil |
| funccompile(fn) |
| } |