blob: edb0d6a5330fdd4eaa9b24ca06692bdcc4dc67a5 [file] [log] [blame]
// 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 pkginit
import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/noder"
"cmd/compile/internal/objw"
"cmd/compile/internal/staticinit"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
"fmt"
"os"
)
// MakeInit creates a synthetic init function to handle any
// package-scope initialization statements.
//
// TODO(mdempsky): Move into noder, so that the types2-based frontends
// can use Info.InitOrder instead.
func MakeInit() {
nf := initOrder(typecheck.Target.Decls)
if len(nf) == 0 {
return
}
// Make a function that contains all the initialization statements.
base.Pos = nf[0].Pos() // prolog/epilog gets line number of first init stmt
initializers := typecheck.Lookup("init")
fn := typecheck.DeclFunc(initializers, nil, nil, nil)
for _, dcl := range typecheck.InitTodoFunc.Dcl {
dcl.Curfn = fn
}
fn.Dcl = append(fn.Dcl, typecheck.InitTodoFunc.Dcl...)
typecheck.InitTodoFunc.Dcl = nil
fn.SetIsPackageInit(true)
// Outline (if legal/profitable) global map inits.
newfuncs := []*ir.Func{}
nf, newfuncs = staticinit.OutlineMapInits(nf)
// Suppress useless "can inline" diagnostics.
// Init functions are only called dynamically.
fn.SetInlinabilityChecked(true)
for _, nfn := range newfuncs {
nfn.SetInlinabilityChecked(true)
}
fn.Body = nf
typecheck.FinishFuncBody()
typecheck.Func(fn)
ir.WithFunc(fn, func() {
typecheck.Stmts(nf)
})
typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
if base.Debug.WrapGlobalMapDbg > 1 {
fmt.Fprintf(os.Stderr, "=-= len(newfuncs) is %d for %v\n",
len(newfuncs), fn)
}
for _, nfn := range newfuncs {
if base.Debug.WrapGlobalMapDbg > 1 {
fmt.Fprintf(os.Stderr, "=-= add to target.decls %v\n", nfn)
}
typecheck.Target.Decls = append(typecheck.Target.Decls, ir.Node(nfn))
}
// Prepend to Inits, so it runs first, before any user-declared init
// functions.
typecheck.Target.Inits = append([]*ir.Func{fn}, typecheck.Target.Inits...)
if typecheck.InitTodoFunc.Dcl != nil {
// We only generate temps using InitTodoFunc if there
// are package-scope initialization statements, so
// something's weird if we get here.
base.Fatalf("InitTodoFunc still has declarations")
}
typecheck.InitTodoFunc = nil
}
// Task makes and returns an initialization record for the package.
// See runtime/proc.go:initTask for its layout.
// The 3 tasks for initialization are:
// 1. Initialize all of the packages the current package depends on.
// 2. Initialize all the variables that have initializers.
// 3. Run any init functions.
func Task() *ir.Name {
var deps []*obj.LSym // initTask records for packages the current package depends on
var fns []*obj.LSym // functions to call for package initialization
// Find imported packages with init tasks.
for _, pkg := range typecheck.Target.Imports {
n, ok := pkg.Lookup(".inittask").Def.(*ir.Name)
if !ok {
continue
}
if n.Op() != ir.ONAME || n.Class != ir.PEXTERN {
base.Fatalf("bad inittask: %v", n)
}
deps = append(deps, n.Linksym())
}
if base.Flag.ASan {
// Make an initialization function to call runtime.asanregisterglobals to register an
// array of instrumented global variables when -asan is enabled. An instrumented global
// variable is described by a structure.
// See the _asan_global structure declared in src/runtime/asan/asan.go.
//
// func init {
// var globals []_asan_global {...}
// asanregisterglobals(&globals[0], len(globals))
// }
for _, n := range typecheck.Target.Externs {
if canInstrumentGlobal(n) {
name := n.Sym().Name
InstrumentGlobalsMap[name] = n
InstrumentGlobalsSlice = append(InstrumentGlobalsSlice, n)
}
}
ni := len(InstrumentGlobalsMap)
if ni != 0 {
// Make an init._ function.
base.Pos = base.AutogeneratedPos
typecheck.DeclContext = ir.PEXTERN
name := noder.Renameinit()
fnInit := typecheck.DeclFunc(name, nil, nil, nil)
// Get an array of instrumented global variables.
globals := instrumentGlobals(fnInit)
// Call runtime.asanregisterglobals function to poison redzones.
// runtime.asanregisterglobals(unsafe.Pointer(&globals[0]), ni)
asanf := typecheck.NewName(ir.Pkgs.Runtime.Lookup("asanregisterglobals"))
ir.MarkFunc(asanf)
asanf.SetType(types.NewSignature(nil, []*types.Field{
types.NewField(base.Pos, nil, types.Types[types.TUNSAFEPTR]),
types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
}, nil))
asancall := ir.NewCallExpr(base.Pos, ir.OCALL, asanf, nil)
asancall.Args.Append(typecheck.ConvNop(typecheck.NodAddr(
ir.NewIndexExpr(base.Pos, globals, ir.NewInt(base.Pos, 0))), types.Types[types.TUNSAFEPTR]))
asancall.Args.Append(typecheck.DefaultLit(ir.NewInt(base.Pos, int64(ni)), types.Types[types.TUINTPTR]))
fnInit.Body.Append(asancall)
typecheck.FinishFuncBody()
typecheck.Func(fnInit)
ir.CurFunc = fnInit
typecheck.Stmts(fnInit.Body)
ir.CurFunc = nil
typecheck.Target.Decls = append(typecheck.Target.Decls, fnInit)
typecheck.Target.Inits = append(typecheck.Target.Inits, fnInit)
}
}
// Record user init functions.
for _, fn := range typecheck.Target.Inits {
if fn.Sym().Name == "init" {
// Synthetic init function for initialization of package-scope
// variables. We can use staticinit to optimize away static
// assignments.
s := staticinit.Schedule{
Plans: make(map[ir.Node]*staticinit.Plan),
Temps: make(map[ir.Node]*ir.Name),
}
for _, n := range fn.Body {
s.StaticInit(n)
}
fn.Body = s.Out
ir.WithFunc(fn, func() {
typecheck.Stmts(fn.Body)
})
if len(fn.Body) == 0 {
fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)}
}
}
// Skip init functions with empty bodies.
if len(fn.Body) == 1 {
if stmt := fn.Body[0]; stmt.Op() == ir.OBLOCK && len(stmt.(*ir.BlockStmt).List) == 0 {
continue
}
}
fns = append(fns, fn.Nname.Linksym())
}
if len(deps) == 0 && len(fns) == 0 && types.LocalPkg.Path != "main" && types.LocalPkg.Path != "runtime" {
return nil // nothing to initialize
}
// Make an .inittask structure.
sym := typecheck.Lookup(".inittask")
task := typecheck.NewName(sym)
task.SetType(types.Types[types.TUINT8]) // fake type
task.Class = ir.PEXTERN
sym.Def = task
lsym := task.Linksym()
ot := 0
ot = objw.Uint32(lsym, ot, 0) // state: not initialized yet
ot = objw.Uint32(lsym, ot, uint32(len(fns)))
for _, f := range fns {
ot = objw.SymPtr(lsym, ot, f, 0)
}
// Add relocations which tell the linker all of the packages
// that this package depends on (and thus, all of the packages
// that need to be initialized before this one).
for _, d := range deps {
r := obj.Addrel(lsym)
r.Type = objabi.R_INITORDER
r.Sym = d
}
// An initTask has pointers, but none into the Go heap.
// It's not quite read only, the state field must be modifiable.
objw.Global(lsym, int32(ot), obj.NOPTR)
return task
}
// initRequiredForCoverage returns TRUE if we need to force creation
// of an init function for the package so as to insert a coverage
// runtime registration call.
func initRequiredForCoverage(l []ir.Node) bool {
if base.Flag.Cfg.CoverageInfo == nil {
return false
}
for _, n := range l {
if n.Op() == ir.ODCLFUNC {
return true
}
}
return false
}