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 import (
      8 	"cmd/compile/internal/types"
      9 )
     10 
     11 // A function named init is a special case.
     12 // It is called by the initialization before main is run.
     13 // To make it unique within a package and also uncallable,
     14 // the name, normally "pkg.init", is altered to "pkg.init.0".
     15 var renameinitgen int
     16 
     17 func renameinit() *types.Sym {
     18 	s := lookupN("init.", renameinitgen)
     19 	renameinitgen++
     20 	return s
     21 }
     22 
     23 // anyinit reports whether there any interesting init statements.
     24 func anyinit(n []*Node) bool {
     25 	for _, ln := range n {
     26 		switch ln.Op {
     27 		case ODCLFUNC, ODCLCONST, ODCLTYPE, OEMPTY:
     28 		case OAS:
     29 			if !isblank(ln.Left) || !candiscard(ln.Right) {
     30 				return true
     31 			}
     32 		default:
     33 			return true
     34 		}
     35 	}
     36 
     37 	// is this main
     38 	if localpkg.Name == "main" {
     39 		return true
     40 	}
     41 
     42 	// is there an explicit init function
     43 	if renameinitgen > 0 {
     44 		return true
     45 	}
     46 
     47 	// are there any imported init functions
     48 	for _, s := range types.InitSyms {
     49 		if s.Def != nil {
     50 			return true
     51 		}
     52 	}
     53 
     54 	// then none
     55 	return false
     56 }
     57 
     58 // fninit hand-crafts package initialization code.
     59 //
     60 //      var initdone uint8                             (1)
     61 //      func init() {                                   (2)
     62 //              if initdone > 1 {                      (3)
     63 //                      return                          (3a)
     64 //              }
     65 //              if initdone == 1 {                     (4)
     66 //                      throw()                         (4a)
     67 //              }
     68 //              initdone = 1                           (5)
     69 //              // over all matching imported symbols
     70 //                      <pkg>.init()                    (6)
     71 //              { <init stmts> }                        (7)
     72 //              init.<n>() // if any                    (8)
     73 //              initdone = 2                           (9)
     74 //              return                                  (10)
     75 //      }
     76 func fninit(n []*Node) {
     77 	lineno = autogeneratedPos
     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.Types[TUINT8], PEXTERN)
     88 
     89 	// (2)
     90 	initsym := lookup("init")
     91 	fn := dclfunc(initsym, nod(OTFUNC, nil, nil))
     92 
     93 	// (3)
     94 	a := nod(OIF, nil, nil)
     95 	a.Left = nod(OGT, gatevar, nodintconst(1))
     96 	a.SetLikely(true)
     97 	r = append(r, a)
     98 	// (3a)
     99 	a.Nbody.Set1(nod(ORETURN, nil, nil))
    100 
    101 	// (4)
    102 	b := nod(OIF, nil, nil)
    103 	b.Left = nod(OEQ, gatevar, nodintconst(1))
    104 	// this actually isn't likely, but code layout is better
    105 	// like this: no JMP needed after the call.
    106 	b.SetLikely(true)
    107 	r = append(r, b)
    108 	// (4a)
    109 	b.Nbody.Set1(nod(OCALL, syslook("throwinit"), nil))
    110 
    111 	// (5)
    112 	a = nod(OAS, gatevar, nodintconst(1))
    113 
    114 	r = append(r, a)
    115 
    116 	// (6)
    117 	for _, s := range types.InitSyms {
    118 		if s.Def != nil && s != initsym {
    119 			n := asNode(s.Def)
    120 			n.checkInitFuncSignature()
    121 			a = nod(OCALL, n, nil)
    122 			r = append(r, a)
    123 		}
    124 	}
    125 
    126 	// (7)
    127 	r = append(r, nf...)
    128 
    129 	// (8)
    130 
    131 	// maxInlineInitCalls is the threshold at which we switch
    132 	// from generating calls inline to generating a static array
    133 	// of functions and calling them in a loop.
    134 	// See CL 41500 for more discussion.
    135 	const maxInlineInitCalls = 500
    136 
    137 	if renameinitgen < maxInlineInitCalls {
    138 		// Not many init functions. Just call them all directly.
    139 		for i := 0; i < renameinitgen; i++ {
    140 			s := lookupN("init.", i)
    141 			n := asNode(s.Def)
    142 			n.checkInitFuncSignature()
    143 			a = nod(OCALL, n, nil)
    144 			r = append(r, a)
    145 		}
    146 	} else {
    147 		// Lots of init functions.
    148 		// Set up an array of functions and loop to call them.
    149 		// This is faster to compile and similar at runtime.
    150 
    151 		// Build type [renameinitgen]func().
    152 		typ := types.NewArray(functype(nil, nil, nil), int64(renameinitgen))
    153 
    154 		// Make and fill array.
    155 		fnarr := staticname(typ)
    156 		fnarr.Name.SetReadonly(true)
    157 		for i := 0; i < renameinitgen; i++ {
    158 			s := lookupN("init.", i)
    159 			lhs := nod(OINDEX, fnarr, nodintconst(int64(i)))
    160 			rhs := asNode(s.Def)
    161 			rhs.checkInitFuncSignature()
    162 			as := nod(OAS, lhs, rhs)
    163 			as = typecheck(as, Etop)
    164 			genAsStatic(as)
    165 		}
    166 
    167 		// Generate a loop that calls each function in turn.
    168 		// for i := 0; i < renameinitgen; i++ {
    169 		//   fnarr[i]()
    170 		// }
    171 		i := temp(types.Types[TINT])
    172 		fnidx := nod(OINDEX, fnarr, i)
    173 		fnidx.SetBounded(true)
    174 
    175 		zero := nod(OAS, i, nodintconst(0))
    176 		cond := nod(OLT, i, nodintconst(int64(renameinitgen)))
    177 		incr := nod(OAS, i, nod(OADD, i, nodintconst(1)))
    178 		body := nod(OCALL, fnidx, nil)
    179 
    180 		loop := nod(OFOR, cond, incr)
    181 		loop.Nbody.Set1(body)
    182 		loop.Ninit.Set1(zero)
    183 
    184 		loop = typecheck(loop, Etop)
    185 		loop = walkstmt(loop)
    186 		r = append(r, loop)
    187 	}
    188 
    189 	// (9)
    190 	a = nod(OAS, gatevar, nodintconst(2))
    191 
    192 	r = append(r, a)
    193 
    194 	// (10)
    195 	a = nod(ORETURN, nil, nil)
    196 
    197 	r = append(r, a)
    198 	exportsym(fn.Func.Nname)
    199 
    200 	fn.Nbody.Set(r)
    201 	funcbody()
    202 
    203 	Curfn = fn
    204 	fn = typecheck(fn, Etop)
    205 	typecheckslice(r, Etop)
    206 	Curfn = nil
    207 	funccompile(fn)
    208 }
    209 
    210 func (n *Node) checkInitFuncSignature() {
    211 	if n.Type.NumRecvs()+n.Type.NumParams()+n.Type.NumResults() > 0 {
    212 		Fatalf("init function cannot have receiver, params, or results: %v (%v)", n, n.Type)
    213 	}
    214 }
    215