Home | History | Annotate | Download | only in gc
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package gc
      6 
      7 // a function named init is a special case.
      8 // it is called by the initialization before
      9 // main is run. to make it unique within a
     10 // package and also uncallable, the name,
     11 // normally "pkg.init", is altered to "pkg.init.1".
     12 
     13 var renameinit_initgen int
     14 
     15 func renameinit() *Sym {
     16 	renameinit_initgen++
     17 	return lookupN("init.", renameinit_initgen)
     18 }
     19 
     20 // hand-craft the following initialization code
     21 //      var initdone uint8                             (1)
     22 //      func init() {                                   (2)
     23 //              if initdone > 1 {                      (3)
     24 //                      return                          (3a)
     25 //              }
     26 //              if initdone == 1 {                     (4)
     27 //                      throw()                         (4a)
     28 //              }
     29 //              initdone = 1                           (5)
     30 //              // over all matching imported symbols
     31 //                      <pkg>.init()                    (6)
     32 //              { <init stmts> }                        (7)
     33 //              init.<n>() // if any                    (8)
     34 //              initdone = 2                           (9)
     35 //              return                                  (10)
     36 //      }
     37 func anyinit(n []*Node) bool {
     38 	// are there any interesting init statements
     39 	for _, ln := range n {
     40 		switch ln.Op {
     41 		case ODCLFUNC, ODCLCONST, ODCLTYPE, OEMPTY:
     42 			break
     43 
     44 		case OAS, OASWB:
     45 			if isblank(ln.Left) && candiscard(ln.Right) {
     46 				break
     47 			}
     48 			fallthrough
     49 		default:
     50 			return true
     51 		}
     52 	}
     53 
     54 	// is this main
     55 	if localpkg.Name == "main" {
     56 		return true
     57 	}
     58 
     59 	// is there an explicit init function
     60 	s := lookup("init.1")
     61 
     62 	if s.Def != nil {
     63 		return true
     64 	}
     65 
     66 	// are there any imported init functions
     67 	for _, s := range initSyms {
     68 		if s.Def != nil {
     69 			return true
     70 		}
     71 	}
     72 
     73 	// then none
     74 	return false
     75 }
     76 
     77 func fninit(n []*Node) {
     78 	nf := initfix(n)
     79 	if !anyinit(nf) {
     80 		return
     81 	}
     82 
     83 	var r []*Node
     84 
     85 	// (1)
     86 	gatevar := newname(lookup("initdone"))
     87 	addvar(gatevar, Types[TUINT8], PEXTERN)
     88 
     89 	// (2)
     90 	Maxarg = 0
     91 
     92 	fn := nod(ODCLFUNC, nil, nil)
     93 	initsym := lookup("init")
     94 	fn.Func.Nname = newname(initsym)
     95 	fn.Func.Nname.Name.Defn = fn
     96 	fn.Func.Nname.Name.Param.Ntype = nod(OTFUNC, nil, nil)
     97 	declare(fn.Func.Nname, PFUNC)
     98 	funchdr(fn)
     99 
    100 	// (3)
    101 	a := nod(OIF, nil, nil)
    102 	a.Left = nod(OGT, gatevar, nodintconst(1))
    103 	a.Likely = 1
    104 	r = append(r, a)
    105 	// (3a)
    106 	a.Nbody.Set1(nod(ORETURN, nil, nil))
    107 
    108 	// (4)
    109 	b := nod(OIF, nil, nil)
    110 	b.Left = nod(OEQ, gatevar, nodintconst(1))
    111 	// this actually isn't likely, but code layout is better
    112 	// like this: no JMP needed after the call.
    113 	b.Likely = 1
    114 	r = append(r, b)
    115 	// (4a)
    116 	b.Nbody.Set1(nod(OCALL, syslook("throwinit"), nil))
    117 
    118 	// (5)
    119 	a = nod(OAS, gatevar, nodintconst(1))
    120 
    121 	r = append(r, a)
    122 
    123 	// (6)
    124 	for _, s := range initSyms {
    125 		if s.Def != nil && s != initsym {
    126 			// could check that it is fn of no args/returns
    127 			a = nod(OCALL, s.Def, nil)
    128 			r = append(r, a)
    129 		}
    130 	}
    131 
    132 	// (7)
    133 	r = append(r, nf...)
    134 
    135 	// (8)
    136 	// could check that it is fn of no args/returns
    137 	for i := 1; ; i++ {
    138 		s := lookupN("init.", i)
    139 		if s.Def == nil {
    140 			break
    141 		}
    142 		a = nod(OCALL, s.Def, nil)
    143 		r = append(r, a)
    144 	}
    145 
    146 	// (9)
    147 	a = nod(OAS, gatevar, nodintconst(2))
    148 
    149 	r = append(r, a)
    150 
    151 	// (10)
    152 	a = nod(ORETURN, nil, nil)
    153 
    154 	r = append(r, a)
    155 	exportsym(fn.Func.Nname)
    156 
    157 	fn.Nbody.Set(r)
    158 	funcbody(fn)
    159 
    160 	Curfn = fn
    161 	fn = typecheck(fn, Etop)
    162 	typecheckslice(r, Etop)
    163 	Curfn = nil
    164 	funccompile(fn)
    165 }
    166