Source file src/cmd/compile/internal/pkginit/init.go

     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 pkginit
     6  
     7  import (
     8  	"cmd/compile/internal/base"
     9  	"cmd/compile/internal/ir"
    10  	"cmd/compile/internal/objw"
    11  	"cmd/compile/internal/staticinit"
    12  	"cmd/compile/internal/typecheck"
    13  	"cmd/compile/internal/types"
    14  	"cmd/internal/obj"
    15  	"cmd/internal/src"
    16  )
    17  
    18  // MakeInit creates a synthetic init function to handle any
    19  // package-scope initialization statements.
    20  //
    21  // TODO(mdempsky): Move into noder, so that the types2-based frontends
    22  // can use Info.InitOrder instead.
    23  func MakeInit() {
    24  	nf := initOrder(typecheck.Target.Decls)
    25  	if len(nf) == 0 {
    26  		return
    27  	}
    28  
    29  	// Make a function that contains all the initialization statements.
    30  	base.Pos = nf[0].Pos() // prolog/epilog gets line number of first init stmt
    31  	initializers := typecheck.Lookup("init")
    32  	fn := typecheck.DeclFunc(initializers, ir.NewFuncType(base.Pos, nil, nil, nil))
    33  	for _, dcl := range typecheck.InitTodoFunc.Dcl {
    34  		dcl.Curfn = fn
    35  	}
    36  	fn.Dcl = append(fn.Dcl, typecheck.InitTodoFunc.Dcl...)
    37  	typecheck.InitTodoFunc.Dcl = nil
    38  
    39  	// Suppress useless "can inline" diagnostics.
    40  	// Init functions are only called dynamically.
    41  	fn.SetInlinabilityChecked(true)
    42  
    43  	fn.Body = nf
    44  	typecheck.FinishFuncBody()
    45  
    46  	typecheck.Func(fn)
    47  	ir.WithFunc(fn, func() {
    48  		typecheck.Stmts(nf)
    49  	})
    50  	typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
    51  
    52  	// Prepend to Inits, so it runs first, before any user-declared init
    53  	// functions.
    54  	typecheck.Target.Inits = append([]*ir.Func{fn}, typecheck.Target.Inits...)
    55  
    56  	if typecheck.InitTodoFunc.Dcl != nil {
    57  		// We only generate temps using InitTodoFunc if there
    58  		// are package-scope initialization statements, so
    59  		// something's weird if we get here.
    60  		base.Fatalf("InitTodoFunc still has declarations")
    61  	}
    62  	typecheck.InitTodoFunc = nil
    63  }
    64  
    65  // Task makes and returns an initialization record for the package.
    66  // See runtime/proc.go:initTask for its layout.
    67  // The 3 tasks for initialization are:
    68  //   1) Initialize all of the packages the current package depends on.
    69  //   2) Initialize all the variables that have initializers.
    70  //   3) Run any init functions.
    71  func Task() *ir.Name {
    72  	var deps []*obj.LSym // initTask records for packages the current package depends on
    73  	var fns []*obj.LSym  // functions to call for package initialization
    74  
    75  	// Find imported packages with init tasks.
    76  	for _, pkg := range typecheck.Target.Imports {
    77  		n := typecheck.Resolve(ir.NewIdent(base.Pos, pkg.Lookup(".inittask")))
    78  		if n.Op() == ir.ONONAME {
    79  			continue
    80  		}
    81  		if n.Op() != ir.ONAME || n.(*ir.Name).Class != ir.PEXTERN {
    82  			base.Fatalf("bad inittask: %v", n)
    83  		}
    84  		deps = append(deps, n.(*ir.Name).Linksym())
    85  	}
    86  
    87  	// Record user init functions.
    88  	for _, fn := range typecheck.Target.Inits {
    89  		if fn.Sym().Name == "init" {
    90  			// Synthetic init function for initialization of package-scope
    91  			// variables. We can use staticinit to optimize away static
    92  			// assignments.
    93  			s := staticinit.Schedule{
    94  				Plans: make(map[ir.Node]*staticinit.Plan),
    95  				Temps: make(map[ir.Node]*ir.Name),
    96  			}
    97  			for _, n := range fn.Body {
    98  				s.StaticInit(n)
    99  			}
   100  			fn.Body = s.Out
   101  			ir.WithFunc(fn, func() {
   102  				typecheck.Stmts(fn.Body)
   103  			})
   104  
   105  			if len(fn.Body) == 0 {
   106  				fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)}
   107  			}
   108  		}
   109  
   110  		// Skip init functions with empty bodies.
   111  		if len(fn.Body) == 1 {
   112  			if stmt := fn.Body[0]; stmt.Op() == ir.OBLOCK && len(stmt.(*ir.BlockStmt).List) == 0 {
   113  				continue
   114  			}
   115  		}
   116  		fns = append(fns, fn.Nname.Linksym())
   117  	}
   118  
   119  	if len(deps) == 0 && len(fns) == 0 && types.LocalPkg.Name != "main" && types.LocalPkg.Name != "runtime" {
   120  		return nil // nothing to initialize
   121  	}
   122  
   123  	// Make an .inittask structure.
   124  	sym := typecheck.Lookup(".inittask")
   125  	task := typecheck.NewName(sym)
   126  	task.SetType(types.Types[types.TUINT8]) // fake type
   127  	task.Class = ir.PEXTERN
   128  	sym.Def = task
   129  	lsym := task.Linksym()
   130  	ot := 0
   131  	ot = objw.Uintptr(lsym, ot, 0) // state: not initialized yet
   132  	ot = objw.Uintptr(lsym, ot, uint64(len(deps)))
   133  	ot = objw.Uintptr(lsym, ot, uint64(len(fns)))
   134  	for _, d := range deps {
   135  		ot = objw.SymPtr(lsym, ot, d, 0)
   136  	}
   137  	for _, f := range fns {
   138  		ot = objw.SymPtr(lsym, ot, f, 0)
   139  	}
   140  	// An initTask has pointers, but none into the Go heap.
   141  	// It's not quite read only, the state field must be modifiable.
   142  	objw.Global(lsym, int32(ot), obj.NOPTR)
   143  	return task
   144  }
   145  

View as plain text