diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/compile/internal/pkginit/init.go | |
parent | Initial commit. (diff) | |
download | golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip |
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/compile/internal/pkginit/init.go')
-rw-r--r-- | src/cmd/compile/internal/pkginit/init.go | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/pkginit/init.go b/src/cmd/compile/internal/pkginit/init.go new file mode 100644 index 0000000..e13a7fb --- /dev/null +++ b/src/cmd/compile/internal/pkginit/init.go @@ -0,0 +1,212 @@ +// 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/src" +) + +// 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 + + // Suppress useless "can inline" diagnostics. + // Init functions are only called dynamically. + fn.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) + + // 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 := typecheck.Resolve(ir.NewIdent(base.Pos, pkg.Lookup(".inittask"))) + if n.Op() == ir.ONONAME { + continue + } + if n.Op() != ir.ONAME || n.(*ir.Name).Class != ir.PEXTERN { + base.Fatalf("bad inittask: %v", n) + } + deps = append(deps, n.(*ir.Name).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 intrumented 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(types.NoPkg, nil, 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(0))), types.Types[types.TUNSAFEPTR])) + asancall.Args.Append(typecheck.ConvNop(ir.NewInt(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.Uintptr(lsym, ot, 0) // state: not initialized yet + ot = objw.Uintptr(lsym, ot, uint64(len(deps))) + ot = objw.Uintptr(lsym, ot, uint64(len(fns))) + for _, d := range deps { + ot = objw.SymPtr(lsym, ot, d, 0) + } + for _, f := range fns { + ot = objw.SymPtr(lsym, ot, f, 0) + } + // 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 +} |