github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/cmd/compile/internal/gc/init.go (about)

     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  }